Merge branch 'master' into expermiental
This commit is contained in:
commit
15465a020f
1
.gitignore
vendored
1
.gitignore
vendored
@ -146,3 +146,4 @@
|
|||||||
/sdk/nbi/stub/ext/components/products/jdk/build/
|
/sdk/nbi/stub/ext/components/products/jdk/build/
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/dist/
|
/sdk/nbi/stub/ext/components/products/jdk/dist/
|
||||||
/sdk/jme3-dark-laf/nbproject/private/
|
/sdk/jme3-dark-laf/nbproject/private/
|
||||||
|
jme3-lwjgl3/build/
|
||||||
|
@ -122,9 +122,13 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
|
|||||||
return Platform.Android_ARM6;
|
return Platform.Android_ARM6;
|
||||||
} else if (arch.contains("v7")) {
|
} else if (arch.contains("v7")) {
|
||||||
return Platform.Android_ARM7;
|
return Platform.Android_ARM7;
|
||||||
|
} else if (arch.contains("v8")) {
|
||||||
|
return Platform.Android_ARM8;
|
||||||
} else {
|
} else {
|
||||||
return Platform.Android_ARM5; // unknown ARM
|
return Platform.Android_ARM5; // unknown ARM
|
||||||
}
|
}
|
||||||
|
} else if (arch.contains("aarch")) {
|
||||||
|
return Platform.Android_ARM8;
|
||||||
} else {
|
} else {
|
||||||
return Platform.Android_Other;
|
return Platform.Android_Other;
|
||||||
}
|
}
|
||||||
|
@ -230,18 +230,22 @@ public class BlenderKey extends ModelKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Not used any more.
|
||||||
* This method sets the asset root path.
|
* This method sets the asset root path.
|
||||||
* @param assetRootPath
|
* @param assetRootPath
|
||||||
* the assets root path
|
* the assets root path
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAssetRootPath(String assetRootPath) {
|
public void setAssetRootPath(String assetRootPath) {
|
||||||
this.assetRootPath = assetRootPath;
|
this.assetRootPath = assetRootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Not used any more.
|
||||||
* This method returns the asset root path.
|
* This method returns the asset root path.
|
||||||
* @return the asset root path
|
* @return the asset root path
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getAssetRootPath() {
|
public String getAssetRootPath() {
|
||||||
return assetRootPath;
|
return assetRootPath;
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene.plugins.blender;
|
package com.jme3.scene.plugins.blender;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -40,25 +38,17 @@ import java.util.Map.Entry;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.animation.Animation;
|
|
||||||
import com.jme3.asset.AssetNotFoundException;
|
import com.jme3.asset.AssetNotFoundException;
|
||||||
import com.jme3.asset.BlenderKey;
|
import com.jme3.asset.BlenderKey;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import com.jme3.light.Light;
|
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.post.Filter;
|
|
||||||
import com.jme3.renderer.Camera;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
|
||||||
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
|
||||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
|
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
|
||||||
@ -157,7 +147,6 @@ public abstract class AbstractBlenderHelper {
|
|||||||
* @throws BlenderFileException
|
* @throws BlenderFileException
|
||||||
* and exception is throw when problems with reading a blend file occur
|
* and exception is throw when problems with reading a blend file occur
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected Object loadLibrary(Structure id) throws BlenderFileException {
|
protected Object loadLibrary(Structure id) throws BlenderFileException {
|
||||||
Pointer pLib = (Pointer) id.getFieldValue("lib");
|
Pointer pLib = (Pointer) id.getFieldValue("lib");
|
||||||
if (pLib.isNotNull()) {
|
if (pLib.isNotNull()) {
|
||||||
@ -167,79 +156,21 @@ public abstract class AbstractBlenderHelper {
|
|||||||
String path = library.getFieldValue("filepath").toString();
|
String path = library.getFieldValue("filepath").toString();
|
||||||
|
|
||||||
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
|
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
|
||||||
File file = new File(path);
|
|
||||||
List<String> pathsToCheck = new ArrayList<String>();
|
|
||||||
String currentPath = file.getName();
|
|
||||||
do {
|
|
||||||
pathsToCheck.add(currentPath);
|
|
||||||
file = file.getParentFile();
|
|
||||||
if (file != null) {
|
|
||||||
currentPath = file.getName() + '/' + currentPath;
|
|
||||||
}
|
|
||||||
} while (file != null);
|
|
||||||
|
|
||||||
Spatial loadedAsset = null;
|
Spatial loadedAsset = null;
|
||||||
BlenderKey blenderKey = null;
|
BlenderKey blenderKey = new BlenderKey(path);
|
||||||
for (String p : pathsToCheck) {
|
|
||||||
blenderKey = new BlenderKey(p);
|
|
||||||
blenderKey.setLoadUnlinkedAssets(true);
|
blenderKey.setLoadUnlinkedAssets(true);
|
||||||
try {
|
try {
|
||||||
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
|
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
|
||||||
break;// break if no exception was thrown
|
|
||||||
} catch (AssetNotFoundException e) {
|
} catch (AssetNotFoundException e) {
|
||||||
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p);
|
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadedAsset != null) {
|
if (loadedAsset != null) {
|
||||||
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
|
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
|
||||||
|
|
||||||
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
|
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
|
||||||
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
|
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
|
||||||
|
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
|
||||||
List<Node> scenes = (List<Node>) entry.getValue().get("scenes");
|
|
||||||
for (Node scene : scenes) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
|
|
||||||
}
|
|
||||||
List<Node> objects = (List<Node>) entry.getValue().get("objects");
|
|
||||||
for (Node object : objects) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
|
|
||||||
}
|
|
||||||
List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes");
|
|
||||||
for (TemporalMesh mesh : meshes) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
|
|
||||||
}
|
|
||||||
List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials");
|
|
||||||
for (MaterialContext materialContext : materials) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
|
|
||||||
}
|
|
||||||
List<Texture> textures = (List<Texture>) entry.getValue().get("textures");
|
|
||||||
for (Texture texture : textures) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
|
|
||||||
}
|
|
||||||
List<Texture> images = (List<Texture>) entry.getValue().get("images");
|
|
||||||
for (Texture image : images) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
|
|
||||||
}
|
|
||||||
List<Animation> animations = (List<Animation>) entry.getValue().get("animations");
|
|
||||||
for (Animation animation : animations) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
|
|
||||||
}
|
|
||||||
List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras");
|
|
||||||
for (Camera camera : cameras) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
|
|
||||||
}
|
|
||||||
List<Light> lights = (List<Light>) entry.getValue().get("lights");
|
|
||||||
for (Light light : lights) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light);
|
|
||||||
}
|
|
||||||
Spatial sky = (Spatial) entry.getValue().get("sky");
|
|
||||||
if (sky != null) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky);
|
|
||||||
}
|
|
||||||
List<Filter> filters = (List<Filter>) entry.getValue().get("filters");
|
|
||||||
for (Filter filter : filters) {
|
|
||||||
blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);
|
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);
|
||||||
|
@ -44,8 +44,11 @@ import com.jme3.animation.Bone;
|
|||||||
import com.jme3.animation.Skeleton;
|
import com.jme3.animation.Skeleton;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.asset.BlenderKey;
|
import com.jme3.asset.BlenderKey;
|
||||||
|
import com.jme3.light.Light;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.post.Filter;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.plugins.blender.animations.BlenderAction;
|
import com.jme3.scene.plugins.blender.animations.BlenderAction;
|
||||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
@ -55,6 +58,8 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
|||||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that stores temporary data and manages it during loading the belnd
|
* The class that stores temporary data and manages it during loading the belnd
|
||||||
@ -77,7 +82,7 @@ public class BlenderContext {
|
|||||||
/** The asset manager. */
|
/** The asset manager. */
|
||||||
private AssetManager assetManager;
|
private AssetManager assetManager;
|
||||||
/** The blocks read from the file. */
|
/** The blocks read from the file. */
|
||||||
protected List<FileBlockHeader> blocks;
|
protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
|
||||||
/**
|
/**
|
||||||
* A map containing the file block headers. The key is the old memory address.
|
* A map containing the file block headers. The key is the old memory address.
|
||||||
*/
|
*/
|
||||||
@ -233,6 +238,7 @@ public class BlenderContext {
|
|||||||
* the block header to store
|
* the block header to store
|
||||||
*/
|
*/
|
||||||
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
|
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
|
||||||
|
blocks.add(fileBlockHeader);
|
||||||
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
|
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
|
||||||
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
|
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
|
||||||
if (headers == null) {
|
if (headers == null) {
|
||||||
@ -242,6 +248,13 @@ public class BlenderContext {
|
|||||||
headers.add(fileBlockHeader);
|
headers.add(fileBlockHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the block headers
|
||||||
|
*/
|
||||||
|
public List<FileBlockHeader> getBlocks() {
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the block header of a given memory address. If the
|
* This method returns the block header of a given memory address. If the
|
||||||
* header is not present then null is returned.
|
* header is not present then null is returned.
|
||||||
@ -332,22 +345,14 @@ public class BlenderContext {
|
|||||||
* The method adds linked content to the blender context.
|
* The method adds linked content to the blender context.
|
||||||
* @param blenderFilePath
|
* @param blenderFilePath
|
||||||
* the path of linked blender file
|
* the path of linked blender file
|
||||||
* @param featureName
|
* @param featureGroup
|
||||||
* the linked feature name
|
* the linked feature group (ie. scenes, materials, meshes, etc.)
|
||||||
* @param feature
|
* @param feature
|
||||||
* the linked feature
|
* the linked feature
|
||||||
*/
|
*/
|
||||||
public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) {
|
@Deprecated
|
||||||
if (feature != null) {
|
public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
|
||||||
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
|
// the method is deprecated and empty at the moment
|
||||||
if (linkedFeatures == null) {
|
|
||||||
linkedFeatures = new HashMap<String, Object>();
|
|
||||||
this.linkedFeatures.put(blenderFilePath, linkedFeatures);
|
|
||||||
}
|
|
||||||
if (!linkedFeatures.containsKey(featureName)) {
|
|
||||||
linkedFeatures.put(featureName, feature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -358,9 +363,106 @@ public class BlenderContext {
|
|||||||
* the feature name we want to get
|
* the feature name we want to get
|
||||||
* @return linked feature or null if none was found
|
* @return linked feature or null if none was found
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Object getLinkedFeature(String blenderFilePath, String featureName) {
|
public Object getLinkedFeature(String blenderFilePath, String featureName) {
|
||||||
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
|
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
|
||||||
return linkedFeatures != null ? linkedFeatures.get(featureName) : null;
|
if(linkedFeatures != null) {
|
||||||
|
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
|
||||||
|
featureName = featureName.substring(2);
|
||||||
|
|
||||||
|
if("SC".equals(namePrefix)) {
|
||||||
|
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
|
||||||
|
if(scenes != null) {
|
||||||
|
for(Node scene : scenes) {
|
||||||
|
if(featureName.equals(scene.getName())) {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("OB".equals(namePrefix)) {
|
||||||
|
List<Node> features = (List<Node>) linkedFeatures.get("objects");
|
||||||
|
if(features != null) {
|
||||||
|
for(Node feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("ME".equals(namePrefix)) {
|
||||||
|
List<Node> features = (List<Node>) linkedFeatures.get("meshes");
|
||||||
|
if(features != null) {
|
||||||
|
for(Node feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("MA".equals(namePrefix)) {
|
||||||
|
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
|
||||||
|
if(features != null) {
|
||||||
|
for(MaterialContext feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("TX".equals(namePrefix)) {
|
||||||
|
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
|
||||||
|
if(features != null) {
|
||||||
|
for(Texture feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("IM".equals(namePrefix)) {
|
||||||
|
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
|
||||||
|
if(features != null) {
|
||||||
|
for(Texture feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("AC".equals(namePrefix)) {
|
||||||
|
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
|
||||||
|
if(features != null) {
|
||||||
|
for(Animation feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("CA".equals(namePrefix)) {
|
||||||
|
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
|
||||||
|
if(features != null) {
|
||||||
|
for(Camera feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("LA".equals(namePrefix)) {
|
||||||
|
List<Light> features = (List<Light>) linkedFeatures.get("lights");
|
||||||
|
if(features != null) {
|
||||||
|
for(Light feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if("FI".equals(featureName)) {
|
||||||
|
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
|
||||||
|
if(features != null) {
|
||||||
|
for(Filter feature : features) {
|
||||||
|
if(featureName.equals(feature.getName())) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -659,4 +761,9 @@ public class BlenderContext {
|
|||||||
public static enum LoadedDataType {
|
public static enum LoadedDataType {
|
||||||
STRUCTURE, FEATURE, TEMPORAL_MESH;
|
STRUCTURE, FEATURE, TEMPORAL_MESH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene.plugins.blender;
|
package com.jme3.scene.plugins.blender;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -41,9 +44,13 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import com.jme3.animation.Animation;
|
import com.jme3.animation.Animation;
|
||||||
import com.jme3.asset.AssetInfo;
|
import com.jme3.asset.AssetInfo;
|
||||||
|
import com.jme3.asset.AssetKey;
|
||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
|
import com.jme3.asset.AssetLocator;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.asset.BlenderKey;
|
import com.jme3.asset.BlenderKey;
|
||||||
import com.jme3.asset.ModelKey;
|
import com.jme3.asset.ModelKey;
|
||||||
|
import com.jme3.asset.StreamAssetInfo;
|
||||||
import com.jme3.light.Light;
|
import com.jme3.light.Light;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.post.Filter;
|
import com.jme3.post.Filter;
|
||||||
@ -81,22 +88,17 @@ import com.jme3.texture.Texture;
|
|||||||
public class BlenderLoader implements AssetLoader {
|
public class BlenderLoader implements AssetLoader {
|
||||||
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
|
||||||
|
|
||||||
/** The blocks read from the file. */
|
|
||||||
protected List<FileBlockHeader> blocks;
|
|
||||||
/** The blender context. */
|
|
||||||
protected BlenderContext blenderContext;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spatial load(AssetInfo assetInfo) throws IOException {
|
public Spatial load(AssetInfo assetInfo) throws IOException {
|
||||||
try {
|
try {
|
||||||
this.setup(assetInfo);
|
BlenderContext blenderContext = this.setup(assetInfo);
|
||||||
|
|
||||||
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
|
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
|
||||||
animationHelper.loadAnimations();
|
animationHelper.loadAnimations();
|
||||||
|
|
||||||
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
||||||
LoadedFeatures loadedFeatures = new LoadedFeatures();
|
LoadedFeatures loadedFeatures = new LoadedFeatures();
|
||||||
for (FileBlockHeader block : blocks) {
|
for (FileBlockHeader block : blenderContext.getBlocks()) {
|
||||||
switch (block.getCode()) {
|
switch (block.getCode()) {
|
||||||
case BLOCK_OB00:
|
case BLOCK_OB00:
|
||||||
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
@ -181,7 +183,7 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
|
|
||||||
LOGGER.fine("Loading scenes and attaching them to the root object.");
|
LOGGER.fine("Loading scenes and attaching them to the root object.");
|
||||||
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
|
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
|
||||||
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext)));
|
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
|
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
|
||||||
@ -220,7 +222,7 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
|
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
this.clear();
|
this.clear(assetInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +230,12 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
* This method converts the given structure to a scene node.
|
* This method converts the given structure to a scene node.
|
||||||
* @param structure
|
* @param structure
|
||||||
* structure of a scene
|
* structure of a scene
|
||||||
|
* @param blenderContext the blender context
|
||||||
* @return scene's node
|
* @return scene's node
|
||||||
* @throws BlenderFileException
|
* @throws BlenderFileException
|
||||||
* an exception throw when problems with blender file occur
|
* an exception throw when problems with blender file occur
|
||||||
*/
|
*/
|
||||||
private Node toScene(Structure structure) throws BlenderFileException {
|
private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
Node result = new Node(structure.getName());
|
Node result = new Node(structure.getName());
|
||||||
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
|
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
|
||||||
@ -265,7 +268,7 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
* @throws BlenderFileException
|
* @throws BlenderFileException
|
||||||
* an exception is throw when something wrong happens with blender file
|
* an exception is throw when something wrong happens with blender file
|
||||||
*/
|
*/
|
||||||
protected void setup(AssetInfo assetInfo) throws BlenderFileException {
|
protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
|
||||||
// registering loaders
|
// registering loaders
|
||||||
ModelKey modelKey = (ModelKey) assetInfo.getKey();
|
ModelKey modelKey = (ModelKey) assetInfo.getKey();
|
||||||
BlenderKey blenderKey;
|
BlenderKey blenderKey;
|
||||||
@ -273,16 +276,15 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
blenderKey = (BlenderKey) modelKey;
|
blenderKey = (BlenderKey) modelKey;
|
||||||
} else {
|
} else {
|
||||||
blenderKey = new BlenderKey(modelKey.getName());
|
blenderKey = new BlenderKey(modelKey.getName());
|
||||||
blenderKey.setAssetRootPath(modelKey.getFolder());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// opening stream
|
// opening stream
|
||||||
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
|
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
|
||||||
|
|
||||||
// reading blocks
|
// reading blocks
|
||||||
blocks = new ArrayList<FileBlockHeader>();
|
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
|
||||||
FileBlockHeader fileBlock;
|
FileBlockHeader fileBlock;
|
||||||
blenderContext = new BlenderContext();
|
BlenderContext blenderContext = new BlenderContext();
|
||||||
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
|
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
|
||||||
blenderContext.setAssetManager(assetInfo.getManager());
|
blenderContext.setAssetManager(assetInfo.getManager());
|
||||||
blenderContext.setInputStream(inputStream);
|
blenderContext.setInputStream(inputStream);
|
||||||
@ -317,15 +319,19 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
if (sceneFileBlock != null) {
|
if (sceneFileBlock != null) {
|
||||||
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
|
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adding locator for linked content
|
||||||
|
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
|
||||||
|
|
||||||
|
return blenderContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The internal data is only needed during loading so make it unreachable so that the GC can release
|
* The internal data is only needed during loading so make it unreachable so that the GC can release
|
||||||
* that memory (which can be quite large amount).
|
* that memory (which can be quite large amount).
|
||||||
*/
|
*/
|
||||||
protected void clear() {
|
protected void clear(AssetInfo assetInfo) {
|
||||||
blenderContext = null;
|
assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
|
||||||
blocks = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -362,4 +368,50 @@ public class BlenderLoader implements AssetLoader {
|
|||||||
*/
|
*/
|
||||||
private ColorRGBA backgroundColor = ColorRGBA.Gray;
|
private ColorRGBA backgroundColor = ColorRGBA.Gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class LinkedContentLocator implements AssetLocator {
|
||||||
|
private File rootFolder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRootPath(String rootPath) {
|
||||||
|
rootFolder = new File(rootPath);
|
||||||
|
if(rootFolder.isFile()) {
|
||||||
|
rootFolder = rootFolder.getParentFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public AssetInfo locate(AssetManager manager, AssetKey key) {
|
||||||
|
if(key instanceof BlenderKey) {
|
||||||
|
File linkedAbsoluteFile = new File(key.getName());
|
||||||
|
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
|
||||||
|
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
|
||||||
|
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
|
||||||
|
try {
|
||||||
|
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ public class LandscapeHelper extends AbstractBlenderHelper {
|
|||||||
if ((mode & MODE_MIST) != 0) {
|
if ((mode & MODE_MIST) != 0) {
|
||||||
LOGGER.fine("Loading fog.");
|
LOGGER.fine("Loading fog.");
|
||||||
result = new FogFilter();
|
result = new FogFilter();
|
||||||
|
result.setName("FIfog");
|
||||||
result.setFogColor(this.toBackgroundColor(worldStructure));
|
result.setFogColor(this.toBackgroundColor(worldStructure));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -46,6 +46,8 @@ import com.jme3.scene.plugins.blender.objects.Properties;
|
|||||||
*/
|
*/
|
||||||
public class TemporalMesh extends Geometry {
|
public class TemporalMesh extends Geometry {
|
||||||
private static final Logger LOGGER = Logger.getLogger(TemporalMesh.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(TemporalMesh.class.getName());
|
||||||
|
/** A minimum weight value. */
|
||||||
|
private static final double MINIMUM_BONE_WEIGHT = FastMath.DBL_EPSILON;
|
||||||
|
|
||||||
/** The blender context. */
|
/** The blender context. */
|
||||||
protected final BlenderContext blenderContext;
|
protected final BlenderContext blenderContext;
|
||||||
@ -530,7 +532,11 @@ public class TemporalMesh extends Geometry {
|
|||||||
for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
|
for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
|
||||||
if (vertexGroupsForVertex.containsKey(entry.getKey())) {
|
if (vertexGroupsForVertex.containsKey(entry.getKey())) {
|
||||||
float weight = vertexGroupsForVertex.get(entry.getKey());
|
float weight = vertexGroupsForVertex.get(entry.getKey());
|
||||||
if (weight > 0) {// no need to use such weights
|
if (weight > MINIMUM_BONE_WEIGHT) {
|
||||||
|
// only values of weight greater than MINIMUM_BONE_WEIGHT are used
|
||||||
|
// if all non zero weights were used, and they were samm enough, problems with normalisation would occur
|
||||||
|
// because adding a very small value to 1.0 will give 1.0
|
||||||
|
// so in order to avoid such errors, which can cause severe animation artifacts we need to use some minimum weight value
|
||||||
boneBuffersForVertex.put(weight, entry.getValue());
|
boneBuffersForVertex.put(weight, entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -737,8 +737,8 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
|
|||||||
setKinematic(capsule.readBoolean("kinematic", false));
|
setKinematic(capsule.readBoolean("kinematic", false));
|
||||||
|
|
||||||
setRestitution(capsule.readFloat("restitution", 0));
|
setRestitution(capsule.readFloat("restitution", 0));
|
||||||
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", Vector3f.NAN.clone());
|
Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", null);
|
||||||
if(angularFactor == Vector3f.NAN) {
|
if(angularFactor == null) {
|
||||||
setAngularFactor(capsule.readFloat("angularFactor", 1));
|
setAngularFactor(capsule.readFloat("angularFactor", 1));
|
||||||
} else {
|
} else {
|
||||||
setAngularFactor(angularFactor);
|
setAngularFactor(angularFactor);
|
||||||
|
@ -59,6 +59,7 @@ class BitmapTextPage extends Geometry {
|
|||||||
|
|
||||||
BitmapTextPage(BitmapFont font, boolean arrayBased, int page) {
|
BitmapTextPage(BitmapFont font, boolean arrayBased, int page) {
|
||||||
super("BitmapFont", new Mesh());
|
super("BitmapFont", new Mesh());
|
||||||
|
setRequiresUpdates(false);
|
||||||
setBatchHint(BatchHint.Never);
|
setBatchHint(BatchHint.Never);
|
||||||
if (font == null) {
|
if (font == null) {
|
||||||
throw new IllegalArgumentException("font cannot be null.");
|
throw new IllegalArgumentException("font cannot be null.");
|
||||||
|
@ -570,6 +570,35 @@ public class Node extends Spatial {
|
|||||||
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
||||||
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
|
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
|
||||||
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
|
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
|
||||||
|
/*
|
||||||
|
I'm removing this change until some issues can be addressed and I really
|
||||||
|
think it needs to be implemented a better way anyway.
|
||||||
|
|
||||||
|
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
||||||
|
and expecting it to trickle down to the children. For example, children
|
||||||
|
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
||||||
|
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
|
||||||
|
collision which isn't resolved. (Having to come up with a collision point in that
|
||||||
|
case is tricky and the first sign that this is the wrong approach.)
|
||||||
|
|
||||||
|
Second, the rippling changes this caused to 'optimize' collideWith() for this
|
||||||
|
special use-case are another sign that this approach was a bit dodgy. The whole
|
||||||
|
idea of calculating a full collision just to see if the two shapes collide at all
|
||||||
|
is very wasteful.
|
||||||
|
|
||||||
|
A proper implementation should support a simpler boolean check that doesn't do
|
||||||
|
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
|
||||||
|
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
|
||||||
|
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
|
||||||
|
|
||||||
|
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
|
||||||
|
code until it can be 'optimized' properly. Hopefully it's not too late to back out
|
||||||
|
the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
|
||||||
|
|
||||||
|
Note: the code itself is relatively simple to implement but I don't have time to
|
||||||
|
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
|
||||||
|
enough to do all the time for > 1.
|
||||||
|
|
||||||
if (children.size() > 4)
|
if (children.size() > 4)
|
||||||
{
|
{
|
||||||
BoundingVolume bv = this.getWorldBound();
|
BoundingVolume bv = this.getWorldBound();
|
||||||
@ -578,6 +607,7 @@ public class Node extends Spatial {
|
|||||||
// collideWith without CollisionResults parameter used to avoid allocation when possible
|
// collideWith without CollisionResults parameter used to avoid allocation when possible
|
||||||
if (bv.collideWith(other) == 0) return 0;
|
if (bv.collideWith(other) == 0) return 0;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
for (Spatial child : children.getArray()){
|
for (Spatial child : children.getArray()){
|
||||||
total += child.collideWith(other, results);
|
total += child.collideWith(other, results);
|
||||||
}
|
}
|
||||||
|
@ -112,13 +112,22 @@ public final class AppSettings extends HashMap<String, Object> {
|
|||||||
public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
|
public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use JogAmp's JOGL as the display system
|
* Use JogAmp's JOGL as the display system, with the OpenGL forward compatible profile
|
||||||
* <p>
|
* <p>
|
||||||
* N.B: This backend is EXPERIMENTAL
|
* N.B: This backend is EXPERIMENTAL
|
||||||
*
|
*
|
||||||
* @see AppSettings#setRenderer(java.lang.String)
|
* @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
|
* Use JogAmp's JOAL as the display system
|
||||||
|
@ -153,6 +153,10 @@ public abstract class JmeSystemDelegate {
|
|||||||
return false;
|
return false;
|
||||||
} else if (arch.equals("universal")) {
|
} else if (arch.equals("universal")) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (arch.equals("aarch32")) {
|
||||||
|
return false;
|
||||||
|
} else if (arch.equals("aarch64")) {
|
||||||
|
return true;
|
||||||
} else if (arch.equals("arm")) {
|
} else if (arch.equals("arm")) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,6 +88,11 @@ public enum Platform {
|
|||||||
*/
|
*/
|
||||||
Android_ARM7,
|
Android_ARM7,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android ARM8
|
||||||
|
*/
|
||||||
|
Android_ARM8,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Android x86
|
* Android x86
|
||||||
*/
|
*/
|
||||||
|
@ -1041,6 +1041,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
capsule.write(mipMapSizes, "mipMapSizes", null);
|
capsule.write(mipMapSizes, "mipMapSizes", null);
|
||||||
capsule.write(multiSamples, "multiSamples", 1);
|
capsule.write(multiSamples, "multiSamples", 1);
|
||||||
capsule.writeByteBufferArrayList(data, "data", null);
|
capsule.writeByteBufferArrayList(data, "data", null);
|
||||||
|
capsule.write(colorSpace, "colorSpace", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void read(JmeImporter e) throws IOException {
|
public void read(JmeImporter e) throws IOException {
|
||||||
@ -1052,6 +1053,7 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
|
|||||||
mipMapSizes = capsule.readIntArray("mipMapSizes", null);
|
mipMapSizes = capsule.readIntArray("mipMapSizes", null);
|
||||||
multiSamples = capsule.readInt("multiSamples", 1);
|
multiSamples = capsule.readInt("multiSamples", 1);
|
||||||
data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null);
|
data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null);
|
||||||
|
colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null);
|
||||||
|
|
||||||
if (mipMapSizes != null) {
|
if (mipMapSizes != null) {
|
||||||
needGeneratedMips = false;
|
needGeneratedMips = false;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
|
|
||||||
#ifdef TEXTURE
|
#ifdef TEXTURE
|
||||||
uniform sampler2D m_Texture;
|
uniform sampler2D m_Texture;
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
@ -6,6 +6,20 @@ MaterialDef Default GUI {
|
|||||||
Boolean VertexColor (UseVertexColor)
|
Boolean VertexColor (UseVertexColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Technique {
|
||||||
|
VertexShader GLSL150: Common/MatDefs/Gui/Gui.vert
|
||||||
|
FragmentShader GLSL150: Common/MatDefs/Gui/Gui.frag
|
||||||
|
|
||||||
|
WorldParameters {
|
||||||
|
WorldViewProjectionMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
Defines {
|
||||||
|
TEXTURE : Texture
|
||||||
|
VERTEX_COLOR : VertexColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Technique {
|
Technique {
|
||||||
VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
|
VertexShader GLSL100: Common/MatDefs/Gui/Gui.vert
|
||||||
FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
|
FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
|
||||||
@ -20,7 +34,4 @@ MaterialDef Default GUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Technique {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
|
|
||||||
uniform mat4 g_WorldViewProjectionMatrix;
|
uniform mat4 g_WorldViewProjectionMatrix;
|
||||||
uniform vec4 m_Color;
|
uniform vec4 m_Color;
|
||||||
|
|
||||||
|
@ -69,3 +69,47 @@ Microsoft\ PC-joystick\ driver.12=POV +Y
|
|||||||
Microsoft\ PC-joystick\ driver.13=POV +X
|
Microsoft\ PC-joystick\ driver.13=POV +X
|
||||||
Microsoft\ PC-joystick\ driver.14=POV -Y
|
Microsoft\ PC-joystick\ driver.14=POV -Y
|
||||||
Microsoft\ PC-joystick\ driver.15=POV -X
|
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
|
||||||
|
@ -309,7 +309,7 @@ public class HDRLoader implements AssetLoader {
|
|||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
dataStore.rewind();
|
dataStore.rewind();
|
||||||
//TODO, HDR color space? considered linear here
|
//HDR files color data is actually stored in linear space.
|
||||||
return new Image(pixelFormat, width, height, dataStore, ColorSpace.Linear);
|
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.Windows32, "native/windows/glfw32.dll");
|
||||||
registerNativeLibrary("glfw-lwjgl3", Platform.Windows64, "native/windows/glfw.dll");
|
registerNativeLibrary("glfw-lwjgl3", Platform.Windows64, "native/windows/glfw.dll");
|
||||||
registerNativeLibrary("glfw-lwjgl3", Platform.Linux32, "native/linux/libglfw32.so");
|
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.MacOSX32, "native/macosx/libglfw.dylib");
|
||||||
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX64, "native/macosx/libglfw.dylib");
|
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX64, "native/macosx/libglfw.dylib");
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
package jme3test.network;
|
package jme3test.network;
|
||||||
|
|
||||||
import com.jme3.network.Client;
|
import com.jme3.network.Client;
|
||||||
|
import com.jme3.network.ClientStateListener;
|
||||||
|
import com.jme3.network.ErrorListener;
|
||||||
import com.jme3.network.Message;
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.MessageListener;
|
import com.jme3.network.MessageListener;
|
||||||
import com.jme3.network.Network;
|
import com.jme3.network.Network;
|
||||||
@ -51,11 +53,11 @@ import jme3test.network.TestChatServer.ChatMessage;
|
|||||||
*/
|
*/
|
||||||
public class TestChatClient extends JFrame {
|
public class TestChatClient extends JFrame {
|
||||||
|
|
||||||
private Client client;
|
private final Client client;
|
||||||
private JEditorPane chatLog;
|
private final JEditorPane chatLog;
|
||||||
private StringBuilder chatMessages = new StringBuilder();
|
private final StringBuilder chatMessages = new StringBuilder();
|
||||||
private JTextField nameField;
|
private final JTextField nameField;
|
||||||
private JTextField messageField;
|
private final JTextField messageField;
|
||||||
|
|
||||||
public TestChatClient(String host) throws IOException {
|
public TestChatClient(String host) throws IOException {
|
||||||
super("jME3 Test Chat Client - to:" + host);
|
super("jME3 Test Chat Client - to:" + host);
|
||||||
@ -90,7 +92,20 @@ public class TestChatClient extends JFrame {
|
|||||||
client = Network.connectToServer(TestChatServer.NAME, TestChatServer.VERSION,
|
client = Network.connectToServer(TestChatServer.NAME, TestChatServer.VERSION,
|
||||||
host, TestChatServer.PORT, TestChatServer.UDP_PORT);
|
host, TestChatServer.PORT, TestChatServer.UDP_PORT);
|
||||||
client.addMessageListener(new ChatHandler(), ChatMessage.class);
|
client.addMessageListener(new ChatHandler(), ChatMessage.class);
|
||||||
|
client.addClientStateListener(new ChatClientStateListener());
|
||||||
|
client.addErrorListener(new ChatErrorListener());
|
||||||
client.start();
|
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) {
|
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 {
|
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
|
// Grab a host string from the user
|
||||||
String s = getString(null, "Host Info", "Enter chat host:", "localhost");
|
String s = getString(null, "Host Info", "Enter chat host:", "localhost");
|
||||||
@ -108,12 +128,23 @@ public class TestChatClient extends JFrame {
|
|||||||
return;
|
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);
|
TestChatClient test = new TestChatClient(s);
|
||||||
test.setVisible(true);
|
test.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChatHandler implements MessageListener<Client> {
|
private class ChatHandler implements MessageListener<Client> {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void messageReceived(Client source, Message m) {
|
public void messageReceived(Client source, Message m) {
|
||||||
ChatMessage chat = (ChatMessage) 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 class SendAction extends AbstractAction {
|
||||||
|
|
||||||
private boolean reliable;
|
private final boolean reliable;
|
||||||
|
|
||||||
public SendAction(boolean reliable) {
|
public SendAction(boolean reliable) {
|
||||||
super(reliable ? "TCP" : "UDP");
|
super(reliable ? "TCP" : "UDP");
|
||||||
this.reliable = reliable;
|
this.reliable = reliable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
String name = nameField.getText();
|
String name = nameField.getText();
|
||||||
String message = messageField.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.*;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
import com.jme3.network.serializing.Serializer;
|
import com.jme3.network.serializing.Serializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple test chat server. When SM implements a set
|
* A simple test chat server. When SM implements a set
|
||||||
@ -51,6 +52,68 @@ public class TestChatServer {
|
|||||||
public static final int PORT = 5110;
|
public static final int PORT = 5110;
|
||||||
public static final int UDP_PORT = 5110;
|
public static final int UDP_PORT = 5110;
|
||||||
|
|
||||||
|
private Server server;
|
||||||
|
private boolean isRunning;
|
||||||
|
|
||||||
|
public TestChatServer() throws IOException {
|
||||||
|
initializeClasses();
|
||||||
|
|
||||||
|
// Use this to test the client/server name version check
|
||||||
|
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() {
|
public static void initializeClasses() {
|
||||||
// Doing it here means that the client code only needs to
|
// Doing it here means that the client code only needs to
|
||||||
// call our initialize.
|
// call our initialize.
|
||||||
@ -58,44 +121,65 @@ public class TestChatServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
public static void main(String... args) throws Exception {
|
||||||
initializeClasses();
|
|
||||||
|
|
||||||
// Use this to test the client/server name version check
|
TestChatServer chatServer = new TestChatServer();
|
||||||
Server server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
|
chatServer.start();
|
||||||
server.start();
|
|
||||||
|
|
||||||
ChatHandler handler = new ChatHandler();
|
System.out.println("Waiting for connections on port:" + PORT);
|
||||||
server.addMessageListener(handler, ChatMessage.class);
|
|
||||||
|
|
||||||
// Keep running basically forever
|
// Keep running basically forever
|
||||||
synchronized (NAME) {
|
while( chatServer.isRunning ) {
|
||||||
NAME.wait();
|
synchronized (chatServer) {
|
||||||
|
chatServer.wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ChatHandler implements MessageListener<HostedConnection> {
|
private class ChatHandler implements MessageListener<HostedConnection> {
|
||||||
|
|
||||||
public ChatHandler() {
|
public ChatHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void messageReceived(HostedConnection source, Message m) {
|
public void messageReceived(HostedConnection source, Message m) {
|
||||||
if (m instanceof ChatMessage) {
|
if (m instanceof ChatMessage) {
|
||||||
// Keep track of the name just in case we
|
// Keep track of the name just in case we
|
||||||
// want to know it for some other reason later and it's
|
// want to know it for some other reason later and it's
|
||||||
// a good example of session data
|
// 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());
|
System.out.println("Broadcasting:" + m + " reliable:" + m.isReliable());
|
||||||
|
|
||||||
// Just rebroadcast... the reliable flag will stay the
|
// Just rebroadcast... the reliable flag will stay the
|
||||||
// same so if it came in on UDP it will go out on that too
|
// same so if it came in on UDP it will go out on that too
|
||||||
source.getServer().broadcast(m);
|
source.getServer().broadcast(cm);
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Received odd message:" + m);
|
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
|
@Serializable
|
||||||
public static class ChatMessage extends AbstractMessage {
|
public static class ChatMessage extends AbstractMessage {
|
||||||
|
|
||||||
@ -126,6 +210,7 @@ public class TestChatServer {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + ":" + message;
|
return name + ":" + message;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ if (!hasProperty('mainClass')) {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':jme3-core')
|
compile project(':jme3-core')
|
||||||
compile project(':jme3-desktop')
|
compile project(':jme3-desktop')
|
||||||
compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.1'
|
compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.2'
|
||||||
compile 'org.jogamp.jogl:jogl-all-main:2.3.1'
|
compile 'org.jogamp.jogl:jogl-all-main:2.3.2'
|
||||||
compile 'org.jogamp.joal:joal-main:2.3.1'
|
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.PixelFormat;
|
||||||
import com.jogamp.nativewindow.util.PixelRectangle;
|
import com.jogamp.nativewindow.util.PixelRectangle;
|
||||||
import com.jogamp.nativewindow.util.Point;
|
import com.jogamp.nativewindow.util.Point;
|
||||||
|
import com.jogamp.newt.event.WindowAdapter;
|
||||||
|
import com.jogamp.newt.event.WindowEvent;
|
||||||
|
|
||||||
public class NewtMouseInput implements MouseInput, MouseListener {
|
public class NewtMouseInput implements MouseInput, MouseListener {
|
||||||
|
|
||||||
@ -106,40 +108,67 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
|
|
||||||
component = comp;
|
component = comp;
|
||||||
component.addMouseListener(this);
|
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() {
|
public void initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setInputListener(RawInputListener listener) {
|
public void setInputListener(RawInputListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public long getInputTimeNanos() {
|
public long getInputTimeNanos() {
|
||||||
return System.nanoTime();
|
return System.nanoTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setCursorVisible(boolean visible) {
|
public void setCursorVisible(boolean visible) {
|
||||||
if (this.visible != visible) {
|
|
||||||
lastKnownLocation.setX(0);
|
lastKnownLocation.setX(0);
|
||||||
lastKnownLocation.setY(0);
|
lastKnownLocation.setY(0);
|
||||||
|
|
||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
component.setPointerVisible(visible);
|
component.setPointerVisible(visible);
|
||||||
if (!visible) {
|
component.confinePointer(!visible);
|
||||||
|
hack_confinePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hack_confinePointer() {
|
||||||
|
if (component.hasFocus() && component.isPointerConfined() && !component.isPointerVisible()) {
|
||||||
recenterMouse(component);
|
recenterMouse(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
|
if (!component.hasFocus()) return;
|
||||||
if (cursorMoved) {
|
if (cursorMoved) {
|
||||||
int newX = location.getX();
|
int newX = location.getX();
|
||||||
int newY = location.getY();
|
int newY = location.getY();
|
||||||
@ -147,7 +176,7 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
|
|
||||||
// invert DY
|
// invert DY
|
||||||
int actualX = lastKnownLocation.getX();
|
int actualX = lastKnownLocation.getX();
|
||||||
int actualY = component.getHeight() - lastKnownLocation.getY();
|
int actualY = component.getSurfaceHeight() - lastKnownLocation.getY();
|
||||||
MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY,
|
MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY,
|
||||||
newX - lastEventX,
|
newX - lastEventX,
|
||||||
lastEventY - newY,
|
lastEventY - newY,
|
||||||
@ -173,43 +202,46 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getButtonCount() {
|
public int getButtonCount() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseClicked(MouseEvent awtEvt) {
|
public void mouseClicked(MouseEvent awtEvt) {
|
||||||
// MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
|
// MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(arg0), false);
|
||||||
// listener.onMouseButtonEvent(evt);
|
// listener.onMouseButtonEvent(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mousePressed(MouseEvent newtEvt) {
|
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());
|
evt.setTime(newtEvt.getWhen());
|
||||||
synchronized (eventQueue) {
|
synchronized (eventQueue) {
|
||||||
eventQueue.add(evt);
|
eventQueue.add(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseReleased(MouseEvent awtEvt) {
|
public void mouseReleased(MouseEvent awtEvt) {
|
||||||
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(awtEvt), false, awtEvt.getX(), awtEvt.getY());
|
MouseButtonEvent evt = new MouseButtonEvent(getJMEButtonIndex(awtEvt), false, awtEvt.getX(), component.getSurfaceHeight() - awtEvt.getY());
|
||||||
evt.setTime(awtEvt.getWhen());
|
evt.setTime(awtEvt.getWhen());
|
||||||
synchronized (eventQueue) {
|
synchronized (eventQueue) {
|
||||||
eventQueue.add(evt);
|
eventQueue.add(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseEntered(MouseEvent awtEvt) {
|
public void mouseEntered(MouseEvent awtEvt) {
|
||||||
if (!visible) {
|
hack_confinePointer();
|
||||||
recenterMouse(component);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseExited(MouseEvent awtEvt) {
|
public void mouseExited(MouseEvent awtEvt) {
|
||||||
if (!visible) {
|
hack_confinePointer();
|
||||||
recenterMouse(component);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseWheelMoved(MouseEvent awtEvt) {
|
public void mouseWheelMoved(MouseEvent awtEvt) {
|
||||||
//FIXME not sure this is the right way to handle this case
|
//FIXME not sure this is the right way to handle this case
|
||||||
// [0] should be used when the shift key is down
|
// [0] should be used when the shift key is down
|
||||||
@ -218,10 +250,12 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
cursorMoved = true;
|
cursorMoved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseDragged(MouseEvent awtEvt) {
|
public void mouseDragged(MouseEvent awtEvt) {
|
||||||
mouseMoved(awtEvt);
|
mouseMoved(awtEvt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void mouseMoved(MouseEvent awtEvt) {
|
public void mouseMoved(MouseEvent awtEvt) {
|
||||||
if (isRecentering) {
|
if (isRecentering) {
|
||||||
// MHenze (cylab) Fix Issue 35:
|
// MHenze (cylab) Fix Issue 35:
|
||||||
@ -239,9 +273,7 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
int dy = awtEvt.getY() - lastKnownLocation.getY();
|
int dy = awtEvt.getY() - lastKnownLocation.getY();
|
||||||
location.setX(location.getX() + dx);
|
location.setX(location.getX() + dx);
|
||||||
location.setY(location.getY() + dy);
|
location.setY(location.getY() + dy);
|
||||||
if (!visible) {
|
hack_confinePointer();
|
||||||
recenterMouse(component);
|
|
||||||
}
|
|
||||||
lastKnownLocation.setX(awtEvt.getX());
|
lastKnownLocation.setX(awtEvt.getX());
|
||||||
lastKnownLocation.setY(awtEvt.getY());
|
lastKnownLocation.setY(awtEvt.getY());
|
||||||
|
|
||||||
@ -253,8 +285,8 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
private void recenterMouse(final GLWindow component) {
|
private void recenterMouse(final GLWindow component) {
|
||||||
eventsSinceRecenter = 0;
|
eventsSinceRecenter = 0;
|
||||||
isRecentering = true;
|
isRecentering = true;
|
||||||
centerLocation.setX(component.getWidth() / 2);
|
centerLocation.setX(component.getSurfaceWidth() / 2);
|
||||||
centerLocation.setY(component.getHeight() / 2);
|
centerLocation.setY(component.getSurfaceHeight() / 2);
|
||||||
centerLocationOnScreen.setX(centerLocation.getX());
|
centerLocationOnScreen.setX(centerLocation.getX());
|
||||||
centerLocationOnScreen.setY(centerLocation.getY());
|
centerLocationOnScreen.setY(centerLocation.getY());
|
||||||
|
|
||||||
@ -287,12 +319,13 @@ public class NewtMouseInput implements MouseInput, MouseListener {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setNativeCursor(JmeCursor cursor) {
|
public void setNativeCursor(JmeCursor cursor) {
|
||||||
final ByteBuffer pixels = Buffers.copyIntBufferAsByteBuffer(cursor.getImagesData());
|
final ByteBuffer pixels = Buffers.copyIntBufferAsByteBuffer(cursor.getImagesData());
|
||||||
final DimensionImmutable size = new Dimension(cursor.getWidth(), cursor.getHeight());
|
final DimensionImmutable size = new Dimension(cursor.getWidth(), cursor.getHeight());
|
||||||
final PixelFormat pixFormat = PixelFormat.RGBA8888;
|
final PixelFormat pixFormat = PixelFormat.RGBA8888;
|
||||||
final PixelRectangle.GenericPixelRect rec = new PixelRectangle.GenericPixelRect(pixFormat, size, 0, true, pixels);
|
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);
|
component.setPointerIcon(joglCursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -532,6 +532,15 @@ public class JoglGL implements GL, GL2, GL3, GL4 {
|
|||||||
@Override
|
@Override
|
||||||
public void glShaderSource(int param1, String[] param2, IntBuffer param3) {
|
public void glShaderSource(int param1, String[] param2, IntBuffer param3) {
|
||||||
checkLimit(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);
|
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.TouchInput;
|
||||||
import com.jme3.input.awt.AwtKeyInput;
|
import com.jme3.input.awt.AwtKeyInput;
|
||||||
import com.jme3.input.awt.AwtMouseInput;
|
import com.jme3.input.awt.AwtMouseInput;
|
||||||
|
import com.jme3.system.AppSettings;
|
||||||
import com.jogamp.opengl.util.Animator;
|
import com.jogamp.opengl.util.Animator;
|
||||||
import com.jogamp.opengl.util.AnimatorBase;
|
import com.jogamp.opengl.util.AnimatorBase;
|
||||||
import com.jogamp.opengl.util.FPSAnimator;
|
import com.jogamp.opengl.util.FPSAnimator;
|
||||||
@ -80,9 +81,13 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent
|
|||||||
|
|
||||||
device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||||
|
|
||||||
//FIXME use the settings to know whether to use the max programmable profile
|
GLCapabilities caps;
|
||||||
//then call GLProfile.getMaxProgrammable(true);
|
if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) {
|
||||||
GLCapabilities caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true));
|
caps = new GLCapabilities(GLProfile.getMaxProgrammable(true));
|
||||||
|
} else {
|
||||||
|
caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true));
|
||||||
|
}
|
||||||
|
|
||||||
caps.setHardwareAccelerated(true);
|
caps.setHardwareAccelerated(true);
|
||||||
caps.setDoubleBuffered(true);
|
caps.setDoubleBuffered(true);
|
||||||
caps.setStencilBits(settings.getStencilBits());
|
caps.setStencilBits(settings.getStencilBits());
|
||||||
|
@ -167,7 +167,7 @@ public abstract class JoglContext implements JmeContext {
|
|||||||
"required for jMonkeyEngine");
|
"required for jMonkeyEngine");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.getRenderer().equals("JOGL")) {
|
if (settings.getRenderer().startsWith("JOGL")) {
|
||||||
com.jme3.renderer.opengl.GL gl = new JoglGL();
|
com.jme3.renderer.opengl.GL gl = new JoglGL();
|
||||||
GLExt glext = new JoglGLExt();
|
GLExt glext = new JoglGLExt();
|
||||||
GLFbo glfbo = new JoglGLFbo();
|
GLFbo glfbo = new JoglGLFbo();
|
||||||
|
@ -37,6 +37,7 @@ import com.jme3.input.MouseInput;
|
|||||||
import com.jme3.input.TouchInput;
|
import com.jme3.input.TouchInput;
|
||||||
import com.jme3.input.jogl.NewtKeyInput;
|
import com.jme3.input.jogl.NewtKeyInput;
|
||||||
import com.jme3.input.jogl.NewtMouseInput;
|
import com.jme3.input.jogl.NewtMouseInput;
|
||||||
|
import com.jme3.system.AppSettings;
|
||||||
import com.jogamp.newt.opengl.GLWindow;
|
import com.jogamp.newt.opengl.GLWindow;
|
||||||
import com.jogamp.opengl.util.Animator;
|
import com.jogamp.opengl.util.Animator;
|
||||||
import com.jogamp.opengl.util.AnimatorBase;
|
import com.jogamp.opengl.util.AnimatorBase;
|
||||||
@ -73,10 +74,12 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE
|
|||||||
|
|
||||||
protected void initGLCanvas() {
|
protected void initGLCanvas() {
|
||||||
loadNatives();
|
loadNatives();
|
||||||
//FIXME use the settings to know whether to use the max programmable profile
|
GLCapabilities caps;
|
||||||
//then call GLProfile.getMaxProgrammable(true);
|
if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) {
|
||||||
//FIXME use the default profile only on embedded devices
|
caps = new GLCapabilities(GLProfile.getMaxProgrammable(true));
|
||||||
GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
|
} else {
|
||||||
|
caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true));
|
||||||
|
}
|
||||||
caps.setHardwareAccelerated(true);
|
caps.setHardwareAccelerated(true);
|
||||||
caps.setDoubleBuffered(true);
|
caps.setDoubleBuffered(true);
|
||||||
caps.setStencilBits(settings.getStencilBits());
|
caps.setStencilBits(settings.getStencilBits());
|
||||||
|
@ -37,16 +37,18 @@ import com.jme3.input.MouseInput;
|
|||||||
import com.jme3.input.TouchInput;
|
import com.jme3.input.TouchInput;
|
||||||
import com.jme3.input.dummy.DummyKeyInput;
|
import com.jme3.input.dummy.DummyKeyInput;
|
||||||
import com.jme3.input.dummy.DummyMouseInput;
|
import com.jme3.input.dummy.DummyMouseInput;
|
||||||
import com.jogamp.newt.NewtVersion;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jogamp.opengl.GL;
|
import com.jogamp.opengl.GL;
|
||||||
import com.jogamp.opengl.GLCapabilities;
|
import com.jogamp.opengl.GLCapabilities;
|
||||||
import com.jogamp.opengl.GLContext;
|
import com.jogamp.opengl.GLContext;
|
||||||
import com.jogamp.opengl.GLDrawableFactory;
|
import com.jogamp.opengl.GLDrawableFactory;
|
||||||
import com.jogamp.opengl.GLOffscreenAutoDrawable;
|
import com.jogamp.opengl.GLOffscreenAutoDrawable;
|
||||||
import com.jogamp.opengl.GLProfile;
|
import com.jogamp.opengl.GLProfile;
|
||||||
|
import com.jogamp.opengl.JoglVersion;
|
||||||
|
|
||||||
|
|
||||||
public class JoglOffscreenBuffer extends JoglContext implements Runnable {
|
public class JoglOffscreenBuffer extends JoglContext implements Runnable {
|
||||||
@ -123,7 +125,7 @@ public class JoglOffscreenBuffer extends JoglContext implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run(){
|
public void run(){
|
||||||
loadNatives();
|
loadNatives();
|
||||||
logger.log(Level.FINE, "Using JOGL {0}", NewtVersion.getInstance().getImplementationVersion());
|
logger.log(Level.FINE, "Using JOGL {0}", JoglVersion.getInstance().getImplementationVersion());
|
||||||
initInThread();
|
initInThread();
|
||||||
while (!needClose.get()){
|
while (!needClose.get()){
|
||||||
runLoop();
|
runLoop();
|
||||||
|
@ -67,18 +67,18 @@ public class DefaultClient implements Client
|
|||||||
private static final int CH_UNRELIABLE = 1;
|
private static final int CH_UNRELIABLE = 1;
|
||||||
private static final int CH_FIRST = 2;
|
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 int id = -1;
|
||||||
private boolean isRunning = false;
|
private boolean isRunning = false;
|
||||||
private CountDownLatch connecting = new CountDownLatch(1);
|
private final CountDownLatch connecting = new CountDownLatch(1);
|
||||||
private String gameName;
|
private String gameName;
|
||||||
private int version;
|
private int version;
|
||||||
private MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
|
private final MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
|
||||||
private List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
|
private final List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
|
||||||
private List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
|
private final List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
|
||||||
private Redispatch dispatcher = new Redispatch();
|
private final Redispatch dispatcher = new Redispatch();
|
||||||
private List<ConnectorAdapter> channels = new ArrayList<ConnectorAdapter>();
|
private final List<ConnectorAdapter> channels = new ArrayList<ConnectorAdapter>();
|
||||||
|
|
||||||
private ConnectorFactory connectorFactory;
|
private ConnectorFactory connectorFactory;
|
||||||
|
|
||||||
@ -100,6 +100,7 @@ public class DefaultClient implements Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void addStandardServices() {
|
protected void addStandardServices() {
|
||||||
|
log.fine("Adding standard services...");
|
||||||
services.addService(new ClientSerializerRegistrationsService());
|
services.addService(new ClientSerializerRegistrationsService());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +129,7 @@ public class DefaultClient implements Client
|
|||||||
throw new IllegalStateException( "Client is not started." );
|
throw new IllegalStateException( "Client is not started." );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
if( isRunning )
|
if( isRunning )
|
||||||
@ -179,6 +181,7 @@ public class DefaultClient implements Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isStarted() {
|
public boolean isStarted() {
|
||||||
return isRunning;
|
return isRunning;
|
||||||
}
|
}
|
||||||
@ -195,33 +198,42 @@ public class DefaultClient implements Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isConnected()
|
public boolean isConnected()
|
||||||
{
|
{
|
||||||
return id != -1 && isRunning;
|
return id != -1 && isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getId()
|
public int getId()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getGameName()
|
public String getGameName()
|
||||||
{
|
{
|
||||||
return gameName;
|
return gameName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getVersion()
|
public int getVersion()
|
||||||
{
|
{
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ClientServiceManager getServices()
|
public ClientServiceManager getServices()
|
||||||
{
|
{
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void send( Message message )
|
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 ) {
|
if( message.isReliable() || channels.get(CH_UNRELIABLE) == null ) {
|
||||||
send(CH_RELIABLE, message, true);
|
send(CH_RELIABLE, message, true);
|
||||||
} else {
|
} else {
|
||||||
@ -229,8 +241,12 @@ public class DefaultClient implements Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void send( int channel, Message message )
|
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 ) {
|
if( channel >= 0 ) {
|
||||||
// Make sure we aren't still connecting. Channels
|
// Make sure we aren't still connecting. Channels
|
||||||
// won't be valid until we are fully connected since
|
// won't be valid until we are fully connected since
|
||||||
@ -275,6 +291,7 @@ public class DefaultClient implements Client
|
|||||||
channels.get(channel).write(buffer);
|
channels.get(channel).write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
checkRunning();
|
checkRunning();
|
||||||
@ -287,9 +304,11 @@ public class DefaultClient implements Client
|
|||||||
if( !isRunning )
|
if( !isRunning )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if( services.isStarted() ) {
|
||||||
// Let the services get a chance to stop before we
|
// Let the services get a chance to stop before we
|
||||||
// kill the connection.
|
// kill the connection.
|
||||||
services.stop();
|
services.stop();
|
||||||
|
}
|
||||||
|
|
||||||
// Send a close message
|
// Send a close message
|
||||||
|
|
||||||
@ -313,41 +332,49 @@ public class DefaultClient implements Client
|
|||||||
services.terminate();
|
services.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addClientStateListener( ClientStateListener listener )
|
public void addClientStateListener( ClientStateListener listener )
|
||||||
{
|
{
|
||||||
stateListeners.add( listener );
|
stateListeners.add( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeClientStateListener( ClientStateListener listener )
|
public void removeClientStateListener( ClientStateListener listener )
|
||||||
{
|
{
|
||||||
stateListeners.remove( listener );
|
stateListeners.remove( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addMessageListener( MessageListener<? super Client> listener )
|
public void addMessageListener( MessageListener<? super Client> listener )
|
||||||
{
|
{
|
||||||
messageListeners.addMessageListener( listener );
|
messageListeners.addMessageListener( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addMessageListener( MessageListener<? super Client> listener, Class... classes )
|
public void addMessageListener( MessageListener<? super Client> listener, Class... classes )
|
||||||
{
|
{
|
||||||
messageListeners.addMessageListener( listener, classes );
|
messageListeners.addMessageListener( listener, classes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeMessageListener( MessageListener<? super Client> listener )
|
public void removeMessageListener( MessageListener<? super Client> listener )
|
||||||
{
|
{
|
||||||
messageListeners.removeMessageListener( listener );
|
messageListeners.removeMessageListener( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeMessageListener( MessageListener<? super Client> listener, Class... classes )
|
public void removeMessageListener( MessageListener<? super Client> listener, Class... classes )
|
||||||
{
|
{
|
||||||
messageListeners.removeMessageListener( listener, classes );
|
messageListeners.removeMessageListener( listener, classes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addErrorListener( ErrorListener<? super Client> listener )
|
public void addErrorListener( ErrorListener<? super Client> listener )
|
||||||
{
|
{
|
||||||
errorListeners.add( listener );
|
errorListeners.add( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeErrorListener( ErrorListener<? super Client> listener )
|
public void removeErrorListener( ErrorListener<? super Client> listener )
|
||||||
{
|
{
|
||||||
errorListeners.remove( listener );
|
errorListeners.remove( listener );
|
||||||
@ -461,11 +488,13 @@ public class DefaultClient implements Client
|
|||||||
|
|
||||||
protected class Redispatch implements MessageListener<Object>, ErrorListener<Object>
|
protected class Redispatch implements MessageListener<Object>, ErrorListener<Object>
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void messageReceived( Object source, Message m )
|
public void messageReceived( Object source, Message m )
|
||||||
{
|
{
|
||||||
dispatch( m );
|
dispatch( m );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void handleError( Object source, Throwable t )
|
public void handleError( Object source, Throwable t )
|
||||||
{
|
{
|
||||||
// Only doing the DefaultClient.this to make the code
|
// 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 static final int CH_FIRST = 2;
|
||||||
|
|
||||||
private boolean isRunning = false;
|
private boolean isRunning = false;
|
||||||
private AtomicInteger nextId = new AtomicInteger(0);
|
private final AtomicInteger nextId = new AtomicInteger(0);
|
||||||
private String gameName;
|
private String gameName;
|
||||||
private int version;
|
private int version;
|
||||||
private KernelFactory kernelFactory = KernelFactory.DEFAULT;
|
private final KernelFactory kernelFactory = KernelFactory.DEFAULT;
|
||||||
private KernelAdapter reliableAdapter;
|
private KernelAdapter reliableAdapter;
|
||||||
private KernelAdapter fastAdapter;
|
private KernelAdapter fastAdapter;
|
||||||
private List<KernelAdapter> channels = new ArrayList<KernelAdapter>();
|
private final List<KernelAdapter> channels = new ArrayList<KernelAdapter>();
|
||||||
private List<Integer> alternatePorts = new ArrayList<Integer>();
|
private final List<Integer> alternatePorts = new ArrayList<Integer>();
|
||||||
private Redispatch dispatcher = new Redispatch();
|
private final Redispatch dispatcher = new Redispatch();
|
||||||
private Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>();
|
private final Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>();
|
||||||
private Map<Endpoint,HostedConnection> endpointConnections
|
private final Map<Endpoint,HostedConnection> endpointConnections
|
||||||
= new ConcurrentHashMap<Endpoint,HostedConnection>();
|
= new ConcurrentHashMap<Endpoint,HostedConnection>();
|
||||||
|
|
||||||
// Keeps track of clients for whom we've only received the UDP
|
// Keeps track of clients for whom we've only received the UDP
|
||||||
// registration message
|
// 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>();
|
= new MessageListenerRegistry<HostedConnection>();
|
||||||
private List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
|
private final List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
|
||||||
|
|
||||||
private HostedServiceManager services;
|
private HostedServiceManager services;
|
||||||
|
|
||||||
@ -108,24 +108,29 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void addStandardServices() {
|
protected void addStandardServices() {
|
||||||
|
log.fine("Adding standard services...");
|
||||||
services.addService(new ServerSerializerRegistrationsService());
|
services.addService(new ServerSerializerRegistrationsService());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getGameName()
|
public String getGameName()
|
||||||
{
|
{
|
||||||
return gameName;
|
return gameName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getVersion()
|
public int getVersion()
|
||||||
{
|
{
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public HostedServiceManager getServices()
|
public HostedServiceManager getServices()
|
||||||
{
|
{
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int addChannel( int port )
|
public int addChannel( int port )
|
||||||
{
|
{
|
||||||
if( isRunning )
|
if( isRunning )
|
||||||
@ -164,6 +169,7 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
if( isRunning )
|
if( isRunning )
|
||||||
@ -185,11 +191,13 @@ public class DefaultServer implements Server
|
|||||||
services.start();
|
services.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isRunning()
|
public boolean isRunning()
|
||||||
{
|
{
|
||||||
return isRunning;
|
return isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
if( !isRunning )
|
if( !isRunning )
|
||||||
@ -214,13 +222,19 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void broadcast( Message message )
|
public void broadcast( Message message )
|
||||||
{
|
{
|
||||||
broadcast( null, message );
|
broadcast( null, message );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void broadcast( Filter<? super HostedConnection> filter, Message message )
|
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() )
|
if( connections.isEmpty() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -237,8 +251,13 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void broadcast( int channel, Filter<? super HostedConnection> filter, Message message )
|
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() )
|
if( connections.isEmpty() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -251,46 +270,55 @@ public class DefaultServer implements Server
|
|||||||
channels.get(channel+CH_FIRST).broadcast( adapter, buffer, true, false );
|
channels.get(channel+CH_FIRST).broadcast( adapter, buffer, true, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public HostedConnection getConnection( int id )
|
public HostedConnection getConnection( int id )
|
||||||
{
|
{
|
||||||
return connections.get(id);
|
return connections.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasConnections()
|
public boolean hasConnections()
|
||||||
{
|
{
|
||||||
return !connections.isEmpty();
|
return !connections.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Collection<HostedConnection> getConnections()
|
public Collection<HostedConnection> getConnections()
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values());
|
return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addConnectionListener( ConnectionListener listener )
|
public void addConnectionListener( ConnectionListener listener )
|
||||||
{
|
{
|
||||||
connectionListeners.add(listener);
|
connectionListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeConnectionListener( ConnectionListener listener )
|
public void removeConnectionListener( ConnectionListener listener )
|
||||||
{
|
{
|
||||||
connectionListeners.remove(listener);
|
connectionListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addMessageListener( MessageListener<? super HostedConnection> listener )
|
public void addMessageListener( MessageListener<? super HostedConnection> listener )
|
||||||
{
|
{
|
||||||
messageListeners.addMessageListener( listener );
|
messageListeners.addMessageListener( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
|
public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
|
||||||
{
|
{
|
||||||
messageListeners.addMessageListener( listener, classes );
|
messageListeners.addMessageListener( listener, classes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeMessageListener( MessageListener<? super HostedConnection> listener )
|
public void removeMessageListener( MessageListener<? super HostedConnection> listener )
|
||||||
{
|
{
|
||||||
messageListeners.removeMessageListener( listener );
|
messageListeners.removeMessageListener( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
|
public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
|
||||||
{
|
{
|
||||||
messageListeners.removeMessageListener( listener, classes );
|
messageListeners.removeMessageListener( listener, classes );
|
||||||
@ -484,12 +512,12 @@ public class DefaultServer implements Server
|
|||||||
|
|
||||||
protected class Connection implements HostedConnection
|
protected class Connection implements HostedConnection
|
||||||
{
|
{
|
||||||
private int id;
|
private final int id;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
private Endpoint[] channels;
|
private Endpoint[] channels;
|
||||||
private int setChannelCount = 0;
|
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 )
|
public Connection( int channelCount )
|
||||||
{
|
{
|
||||||
@ -523,23 +551,30 @@ public class DefaultServer implements Server
|
|||||||
return setChannelCount == channels.length;
|
return setChannelCount == channels.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Server getServer()
|
public Server getServer()
|
||||||
{
|
{
|
||||||
return DefaultServer.this;
|
return DefaultServer.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getId()
|
public int getId()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getAddress()
|
public String getAddress()
|
||||||
{
|
{
|
||||||
return channels[CH_RELIABLE] == null ? null : channels[CH_RELIABLE].getAddress();
|
return channels[CH_RELIABLE] == null ? null : channels[CH_RELIABLE].getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void send( Message message )
|
public void send( Message message )
|
||||||
{
|
{
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.log(Level.FINER, "send({0})", message);
|
||||||
|
}
|
||||||
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
|
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
|
||||||
if( message.isReliable() || channels[CH_UNRELIABLE] == null ) {
|
if( message.isReliable() || channels[CH_UNRELIABLE] == null ) {
|
||||||
channels[CH_RELIABLE].send( buffer );
|
channels[CH_RELIABLE].send( buffer );
|
||||||
@ -548,8 +583,12 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void send( int channel, Message message )
|
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);
|
checkChannel(channel);
|
||||||
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
|
ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null);
|
||||||
channels[channel+CH_FIRST].send(buffer);
|
channels[channel+CH_FIRST].send(buffer);
|
||||||
@ -573,6 +612,7 @@ public class DefaultServer implements Server
|
|||||||
fireConnectionRemoved( this );
|
fireConnectionRemoved( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close( String reason )
|
public void close( String reason )
|
||||||
{
|
{
|
||||||
// Send a reason
|
// Send a reason
|
||||||
@ -593,6 +633,7 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object setAttribute( String name, Object value )
|
public Object setAttribute( String name, Object value )
|
||||||
{
|
{
|
||||||
if( value == null )
|
if( value == null )
|
||||||
@ -601,11 +642,13 @@ public class DefaultServer implements Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
public <T> T getAttribute( String name )
|
public <T> T getAttribute( String name )
|
||||||
{
|
{
|
||||||
return (T)sessionData.get(name);
|
return (T)sessionData.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<String> attributeNames()
|
public Set<String> attributeNames()
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableSet(sessionData.keySet());
|
return Collections.unmodifiableSet(sessionData.keySet());
|
||||||
@ -621,6 +664,7 @@ public class DefaultServer implements Server
|
|||||||
|
|
||||||
protected class Redispatch implements MessageListener<HostedConnection>
|
protected class Redispatch implements MessageListener<HostedConnection>
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void messageReceived( HostedConnection source, Message m )
|
public void messageReceived( HostedConnection source, Message m )
|
||||||
{
|
{
|
||||||
dispatch( source, m );
|
dispatch( source, m );
|
||||||
@ -629,13 +673,14 @@ public class DefaultServer implements Server
|
|||||||
|
|
||||||
protected class FilterAdapter implements Filter<Endpoint>
|
protected class FilterAdapter implements Filter<Endpoint>
|
||||||
{
|
{
|
||||||
private Filter<? super HostedConnection> delegate;
|
private final Filter<? super HostedConnection> delegate;
|
||||||
|
|
||||||
public FilterAdapter( Filter<? super HostedConnection> delegate )
|
public FilterAdapter( Filter<? super HostedConnection> delegate )
|
||||||
{
|
{
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean apply( Endpoint input )
|
public boolean apply( Endpoint input )
|
||||||
{
|
{
|
||||||
HostedConnection conn = getConnection( input );
|
HostedConnection conn = getConnection( input );
|
||||||
|
@ -53,7 +53,7 @@ import java.util.LinkedList;
|
|||||||
*/
|
*/
|
||||||
public class MessageProtocol
|
public class MessageProtocol
|
||||||
{
|
{
|
||||||
private LinkedList<Message> messages = new LinkedList<Message>();
|
private final LinkedList<Message> messages = new LinkedList<Message>();
|
||||||
private ByteBuffer current;
|
private ByteBuffer current;
|
||||||
private int size;
|
private int size;
|
||||||
private Byte carry;
|
private Byte carry;
|
||||||
|
@ -133,6 +133,24 @@ public class SerializerRegistrationsMessage extends AbstractMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 ) {
|
for( Registration reg : registrations ) {
|
||||||
log.log( Level.INFO, "Registering:{0}", reg);
|
log.log( Level.INFO, "Registering:{0}", reg);
|
||||||
reg.register();
|
reg.register();
|
||||||
|
@ -45,6 +45,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.jar.Attributes;
|
import java.util.jar.Attributes;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.LogManager;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -403,6 +404,10 @@ public abstract class Serializer {
|
|||||||
if (reg == null) {
|
if (reg == null) {
|
||||||
throw new SerializerException( "Class not registered:" + type );
|
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());
|
buffer.putShort(reg.getId());
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,6 @@ public abstract class AbstractService<S extends ServiceManager> implements Servi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base service manager class from which the HostedServiceManager
|
* The base service manager class from which the HostedServiceManager
|
||||||
@ -44,7 +46,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
*/
|
*/
|
||||||
public abstract class ServiceManager<T> {
|
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;
|
private volatile boolean started = false;
|
||||||
|
|
||||||
protected ServiceManager() {
|
protected ServiceManager() {
|
||||||
@ -76,6 +80,9 @@ public abstract class ServiceManager<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for( Service<T> s : services ) {
|
for( Service<T> s : services ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Starting service:{0}", s);
|
||||||
|
}
|
||||||
s.start();
|
s.start();
|
||||||
}
|
}
|
||||||
started = true;
|
started = true;
|
||||||
@ -96,6 +103,9 @@ public abstract class ServiceManager<T> {
|
|||||||
throw new IllegalStateException(getClass().getSimpleName() + " not started.");
|
throw new IllegalStateException(getClass().getSimpleName() + " not started.");
|
||||||
}
|
}
|
||||||
for( Service<T> s : services ) {
|
for( Service<T> s : services ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Stopping service:{0}", s);
|
||||||
|
}
|
||||||
s.stop();
|
s.stop();
|
||||||
}
|
}
|
||||||
started = false;
|
started = false;
|
||||||
@ -106,9 +116,18 @@ public abstract class ServiceManager<T> {
|
|||||||
* has already been started then the service will also be started.
|
* has already been started then the service will also be started.
|
||||||
*/
|
*/
|
||||||
public <S extends Service<T>> void addService( S s ) {
|
public <S extends Service<T>> void addService( S s ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "addService({0})", s);
|
||||||
|
}
|
||||||
services.add(s);
|
services.add(s);
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Initializing service:{0}", s);
|
||||||
|
}
|
||||||
s.initialize(getParent());
|
s.initialize(getParent());
|
||||||
if( started ) {
|
if( started ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Starting service:{0}", s);
|
||||||
|
}
|
||||||
s.start();
|
s.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,10 +139,19 @@ public abstract class ServiceManager<T> {
|
|||||||
* the service will be terminated.
|
* the service will be terminated.
|
||||||
*/
|
*/
|
||||||
public <S extends Service<T>> void removeService( S s ) {
|
public <S extends Service<T>> void removeService( S s ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "removeService({0})", s);
|
||||||
|
}
|
||||||
if( started ) {
|
if( started ) {
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Stopping service:{0}", s);
|
||||||
|
}
|
||||||
s.stop();
|
s.stop();
|
||||||
}
|
}
|
||||||
services.remove(s);
|
services.remove(s);
|
||||||
|
if( log.isLoggable(Level.FINE) ) {
|
||||||
|
log.log(Level.FINE, "Terminating service:{0}", s);
|
||||||
|
}
|
||||||
s.terminate(getParent());
|
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.serializing.Serializer;
|
||||||
import com.jme3.network.service.AbstractClientService;
|
import com.jme3.network.service.AbstractClientService;
|
||||||
import com.jme3.network.service.ClientServiceManager;
|
import com.jme3.network.service.ClientServiceManager;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,18 +51,26 @@ import com.jme3.network.service.ClientServiceManager;
|
|||||||
public class ClientSerializerRegistrationsService extends AbstractClientService
|
public class ClientSerializerRegistrationsService extends AbstractClientService
|
||||||
implements MessageListener<Client> {
|
implements MessageListener<Client> {
|
||||||
|
|
||||||
|
static final Logger log = Logger.getLogger(SerializerRegistrationsMessage.class.getName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onInitialize( ClientServiceManager serviceManager ) {
|
protected void onInitialize( ClientServiceManager serviceManager ) {
|
||||||
// Make sure our message type is registered
|
|
||||||
|
// 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
|
// This is the minimum we'd need just to be able to register
|
||||||
// the rest... otherwise we can't even receive this message.
|
// the rest... otherwise we can't even receive this message.
|
||||||
Serializer.registerClass(SerializerRegistrationsMessage.class);
|
Serializer.registerClass(SerializerRegistrationsMessage.class);
|
||||||
Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
|
Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
|
||||||
|
} else {
|
||||||
|
log.log(Level.INFO, "Skipping registration of SerializerRegistrationsMessage.");
|
||||||
|
}
|
||||||
|
|
||||||
// Add our listener for that message type
|
// Add our listener for that message type
|
||||||
serviceManager.getClient().addMessageListener(this, SerializerRegistrationsMessage.class);
|
serviceManager.getClient().addMessageListener(this, SerializerRegistrationsMessage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void messageReceived( Client source, Message m ) {
|
public void messageReceived( Client source, Message m ) {
|
||||||
// We only wait for one kind of message...
|
// We only wait for one kind of message...
|
||||||
SerializerRegistrationsMessage msg = (SerializerRegistrationsMessage)m;
|
SerializerRegistrationsMessage msg = (SerializerRegistrationsMessage)m;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
</Properties>
|
</Properties>
|
||||||
<SyntheticProperties>
|
<SyntheticProperties>
|
||||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||||
|
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||||
</SyntheticProperties>
|
</SyntheticProperties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||||
|
@ -31,9 +31,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.gde.core.properties;
|
package com.jme3.gde.core.properties;
|
||||||
|
|
||||||
import com.jme3.asset.TextureKey;
|
|
||||||
import com.jme3.gde.core.assets.ProjectAssetManager;
|
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.gde.core.util.TreeUtil;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
@ -42,6 +41,7 @@ import java.awt.event.WindowListener;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
import javax.swing.DefaultListSelectionModel;
|
import javax.swing.DefaultListSelectionModel;
|
||||||
@ -52,8 +52,6 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
|||||||
import javax.swing.tree.TreeNode;
|
import javax.swing.tree.TreeNode;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
import javax.swing.tree.TreeSelectionModel;
|
import javax.swing.tree.TreeSelectionModel;
|
||||||
import jme3tools.converters.ImageToAwt;
|
|
||||||
import org.openide.util.ImageUtilities;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays all textures in the ProjectAssetManager,
|
* Displays all textures in the ProjectAssetManager,
|
||||||
@ -68,7 +66,7 @@ public class TextureBrowser extends javax.swing.JDialog implements TreeSelection
|
|||||||
|
|
||||||
private ProjectAssetManager assetManager;
|
private ProjectAssetManager assetManager;
|
||||||
private TexturePropertyEditor editor;
|
private TexturePropertyEditor editor;
|
||||||
private DDSPreview ddsPreview;
|
private TexturePreview texPreview;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
private static final String PREF_LAST_SELECTED = "lastSelectedTexture";
|
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()) {
|
if (node != null && node.isLeaf()) {
|
||||||
String selected = TreeUtil.getPath(node.getUserObjectPath());
|
String selected = TreeUtil.getPath(node.getUserObjectPath());
|
||||||
selected = selected.substring(0, selected.lastIndexOf("/"));
|
selected = selected.substring(0, selected.lastIndexOf("/"));
|
||||||
Texture tex = assetManager.loadTexture(selected);
|
// Texture tex = assetManager.loadTexture(selected);
|
||||||
editor.setValue(tex);
|
// editor.setValue(tex);
|
||||||
editor.setAsText(selected);
|
editor.setAsText(selected);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -270,7 +268,7 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
|
|||||||
|
|
||||||
private void setSelectedTexture(Texture texture) {
|
private void setSelectedTexture(Texture texture) {
|
||||||
if (texture != null) {
|
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("/");
|
String[] path = ("/" + texture.getName()).split("/");
|
||||||
TreePath parent = new TreePath((TreeNode) jTree1.getModel().getRoot());
|
TreePath parent = new TreePath((TreeNode) jTree1.getModel().getRoot());
|
||||||
TreePath selectedTreePath = TreeUtil.buildTreePath(jTree1, parent, path, 0, true);
|
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) {
|
public void valueChanged(TreeSelectionEvent e) {
|
||||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree1.getLastSelectedPathComponent();
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree1.getLastSelectedPathComponent();
|
||||||
|
|
||||||
@ -295,25 +294,14 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Object nodeInfo = node.getUserObject();
|
|
||||||
if (node.isLeaf()) {
|
if (node.isLeaf()) {
|
||||||
String selected = TreeUtil.getPath(node.getUserObjectPath());
|
String selected = TreeUtil.getPath(node.getUserObjectPath());
|
||||||
selected = selected.substring(0, selected.lastIndexOf("/"));
|
selected = selected.substring(0, selected.lastIndexOf("/"));
|
||||||
Icon newicon = null;
|
Icon newicon = null;
|
||||||
if (selected.toLowerCase().endsWith(".dds")) {
|
if (texPreview == null) {
|
||||||
if (ddsPreview == null) {
|
texPreview = new TexturePreview(assetManager);
|
||||||
ddsPreview = new DDSPreview(assetManager);
|
|
||||||
}
|
}
|
||||||
ddsPreview.requestPreview(selected, (String) node.getUserObject(), 450, 450, imagePreviewLabel, infoLabel);
|
texPreview.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());
|
|
||||||
}
|
|
||||||
|
|
||||||
prefs.put(PREF_LAST_SELECTED, selected);
|
prefs.put(PREF_LAST_SELECTED, selected);
|
||||||
} else {
|
} else {
|
||||||
imagePreviewLabel.setIcon(null);
|
imagePreviewLabel.setIcon(null);
|
||||||
@ -323,27 +311,34 @@ private void noTexturebuttonActionPerformed(java.awt.event.ActionEvent evt) {//G
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowOpened(WindowEvent e) {
|
public void windowOpened(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
if (ddsPreview != null) {
|
if (texPreview != null) {
|
||||||
ddsPreview.cleanUp();
|
texPreview.cleanUp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowClosed(WindowEvent e) {
|
public void windowClosed(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowIconified(WindowEvent e) {
|
public void windowIconified(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowDeiconified(WindowEvent e) {
|
public void windowDeiconified(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowActivated(WindowEvent e) {
|
public void windowActivated(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void windowDeactivated(WindowEvent e) {
|
public void windowDeactivated(WindowEvent e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,14 @@ public class TexturePropertyEditor implements PropertyEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
|
if(texture == null && assetKey != null){
|
||||||
|
if (manager == null){
|
||||||
|
manager = SceneApplication.getApplication().getAssetManager();
|
||||||
|
}
|
||||||
|
texture = manager.loadTexture(assetKey);
|
||||||
|
}
|
||||||
return texture;
|
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;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
float depthx=floor(texCoord.x);
|
float depthx = floor(texCoord.x);
|
||||||
float depthy=(m_Rows-1.0) - floor(texCoord.y);
|
float depthy = (float(m_Rows) - 1.0) - floor(texCoord.y);
|
||||||
//vec3 texC=vec3(texCoord.x,texCoord.y ,0.7);//
|
|
||||||
|
|
||||||
vec3 texC=vec3(fract(texCoord.x),fract(texCoord.y),(depthy*m_Rows+depthx)*m_InvDepth);//
|
vec3 texC = vec3(fract(texCoord.x), fract(texCoord.y), (depthy * float(m_Rows) + depthx) * m_InvDepth);//
|
||||||
gl_FragColor= texture3D(m_Texture,texC);
|
gl_FragColor = texture3D(m_Texture, texC);
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ attribute vec3 inPosition;
|
|||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0);
|
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
|
||||||
texCoord=inTexCoord;
|
texCoord = inTexCoord;
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ package com.jme3.gde.materials.multiview.widgets;
|
|||||||
import com.jme3.asset.AssetNotFoundException;
|
import com.jme3.asset.AssetNotFoundException;
|
||||||
import com.jme3.gde.core.assets.ProjectAssetManager;
|
import com.jme3.gde.core.assets.ProjectAssetManager;
|
||||||
import com.jme3.gde.core.properties.TexturePropertyEditor;
|
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.MaterialProperty;
|
||||||
import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
|
import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
@ -39,7 +39,7 @@ public class TexturePanel extends MaterialPropertyWidget {
|
|||||||
private boolean flip = false;
|
private boolean flip = false;
|
||||||
private boolean repeat = false;
|
private boolean repeat = false;
|
||||||
private String textureName = null;
|
private String textureName = null;
|
||||||
private DDSPreview ddsPreview;
|
private TexturePreview texPreview;
|
||||||
private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
|
private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
|
||||||
|
|
||||||
/** Creates new form SelectionPanel */
|
/** Creates new form SelectionPanel */
|
||||||
@ -53,22 +53,13 @@ public class TexturePanel extends MaterialPropertyWidget {
|
|||||||
if (!"".equals(textureName)) {
|
if (!"".equals(textureName)) {
|
||||||
exec.execute(new Runnable() {
|
exec.execute(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try{
|
try{
|
||||||
Texture tex = manager.loadTexture(textureName);
|
if (texPreview == null) {
|
||||||
if (textureName.toLowerCase().endsWith(".dds")) {
|
texPreview = new TexturePreview(manager);
|
||||||
if (ddsPreview == null) {
|
|
||||||
ddsPreview = new DDSPreview(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) {
|
} catch (AssetNotFoundException a) {
|
||||||
Logger.getLogger(MaterialEditorTopComponent.class.getName()).log(Level.WARNING, "Could not load texture {0}", textureName);
|
Logger.getLogger(MaterialEditorTopComponent.class.getName()).log(Level.WARNING, "Could not load texture {0}", textureName);
|
||||||
}
|
}
|
||||||
@ -318,8 +309,8 @@ public class TexturePanel extends MaterialPropertyWidget {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
if (ddsPreview != null) {
|
if (texPreview != null) {
|
||||||
ddsPreview.cleanUp();
|
texPreview.cleanUp();
|
||||||
}
|
}
|
||||||
exec.shutdownNow();
|
exec.shutdownNow();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ package com.jme3.gde.terraineditor;
|
|||||||
import com.jme3.gde.core.assets.AssetDataObject;
|
import com.jme3.gde.core.assets.AssetDataObject;
|
||||||
import com.jme3.gde.core.assets.ProjectAssetManager;
|
import com.jme3.gde.core.assets.ProjectAssetManager;
|
||||||
import com.jme3.gde.core.properties.TexturePropertyEditor;
|
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.PreviewRequest;
|
||||||
import com.jme3.gde.core.scene.SceneApplication;
|
import com.jme3.gde.core.scene.SceneApplication;
|
||||||
import com.jme3.gde.core.scene.SceneListener;
|
import com.jme3.gde.core.scene.SceneListener;
|
||||||
@ -122,7 +122,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
|
|||||||
//private TerrainNodeListener terrainDeletedNodeListener;
|
//private TerrainNodeListener terrainDeletedNodeListener;
|
||||||
private boolean availableNormalTextures;
|
private boolean availableNormalTextures;
|
||||||
private HelpCtx ctx = new HelpCtx("sdk.terrain_editor");
|
private HelpCtx ctx = new HelpCtx("sdk.terrain_editor");
|
||||||
private DDSPreview ddsPreview;
|
private TexturePreview texPreview;
|
||||||
private Map<String, JButton> buttons = new HashMap<String, JButton>();
|
private Map<String, JButton> buttons = new HashMap<String, JButton>();
|
||||||
private JPanel insideToolSettings;
|
private JPanel insideToolSettings;
|
||||||
|
|
||||||
@ -153,35 +153,29 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
|
|||||||
private float max = 0;
|
private float max = 0;
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
@Override
|
||||||
public void incrementProgress(float f) {
|
public void incrementProgress(float f) {
|
||||||
progress += f;
|
progress += f;
|
||||||
progressHandle.progress((int) progress);
|
progressHandle.progress((int) progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setMonitorMax(float f) {
|
public void setMonitorMax(float f) {
|
||||||
max = f;
|
max = f;
|
||||||
// java.awt.EventQueue.invokeLater(new Runnable() {
|
|
||||||
// public void run() {
|
|
||||||
// synchronized(lock){
|
|
||||||
if (progressHandle == null) {
|
if (progressHandle == null) {
|
||||||
progressHandle = ProgressHandleFactory.createHandle("Calculating terrain entropies...");
|
progressHandle = ProgressHandleFactory.createHandle("Calculating terrain entropies...");
|
||||||
progressHandle.start((int) max);
|
progressHandle.start((int) max);
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public float getMonitorMax() {
|
public float getMonitorMax() {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void progressComplete() {
|
public void progressComplete() {
|
||||||
// SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
// public void run() {
|
|
||||||
progressHandle.finish();
|
progressHandle.finish();
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1632,6 +1626,13 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
|
|||||||
|
|
||||||
protected abstract boolean supportsNullTexture();
|
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) {
|
private JButton getButton(Object value, final int row, final int column) {
|
||||||
|
|
||||||
JButton button = buttons.get(row + "-" + column);
|
JButton button = buttons.get(row + "-" + column);
|
||||||
@ -1656,21 +1657,9 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
|
|||||||
}
|
}
|
||||||
|
|
||||||
Texture tex = getTextureFromModel(index); // delegate to sub-class
|
Texture tex = getTextureFromModel(index); // delegate to sub-class
|
||||||
|
|
||||||
//Texture tex = SceneApplication.getApplication().getAssetManager().loadTexture((String)value);
|
|
||||||
if (tex != null) {
|
if (tex != null) {
|
||||||
String selected = tex.getKey().getName();
|
String selected = tex.getKey().getName();
|
||||||
|
getTexturePreview().requestPreview(selected, "", 80, 80, lbl, null);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1694,24 +1683,17 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
|
|||||||
TexturePropertyEditor editor = new TexturePropertyEditor(selectedTex);
|
TexturePropertyEditor editor = new TexturePropertyEditor(selectedTex);
|
||||||
Component view = editor.getCustomEditor();
|
Component view = editor.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
Texture tex = (Texture) editor.getValue();
|
|
||||||
if (editor.getValue() != null) {
|
|
||||||
String selected = tex.getKey().getName();
|
|
||||||
|
|
||||||
if (selected.toLowerCase().endsWith(".dds")) {
|
if (editor.getAsText() != null) {
|
||||||
if (ddsPreview == null) {
|
|
||||||
ddsPreview = new DDSPreview((ProjectAssetManager) SceneApplication.getApplication().getAssetManager());
|
|
||||||
}
|
|
||||||
ddsPreview.requestPreview(selected, "", 80, 80, lbl, null);
|
|
||||||
|
|
||||||
} else {
|
String selected = editor.getAsText();
|
||||||
Icon newicon = ImageUtilities.image2Icon(ImageToAwt.convert(tex.getImage(), false, true, 0));
|
getTexturePreview().requestPreview(selected, "", 80, 80, lbl, null);
|
||||||
lbl.setIcon(newicon);
|
Texture tex = SceneApplication.getApplication().getAssetManager().loadTexture(selected);
|
||||||
}
|
setTextureInModel(row, tex);
|
||||||
} else if (supportsNullTexture()) {
|
} else if (supportsNullTexture()) {
|
||||||
lbl.setIcon(null);
|
lbl.setIcon(null);
|
||||||
}
|
}
|
||||||
setTextureInModel(row, tex);
|
|
||||||
} finally {
|
} finally {
|
||||||
alreadyChoosing = false;
|
alreadyChoosing = false;
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,6 @@ public class TerrainToolController extends SceneToolController {
|
|||||||
*/
|
*/
|
||||||
void doTerrainEditToolActionEnded() {
|
void doTerrainEditToolActionEnded() {
|
||||||
if (terrainTool != null) {
|
if (terrainTool != null) {
|
||||||
System.out.println("undo tagged");
|
|
||||||
terrainTool.actionEnded(jmeRootNode, editorController.getCurrentDataObject());
|
terrainTool.actionEnded(jmeRootNode, editorController.getCurrentDataObject());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,14 +69,16 @@ public class AddSkyboxAction extends AbstractNewSpatialWizardAction {
|
|||||||
} else {
|
} else {
|
||||||
Texture textureSingle = (Texture) wiz.getProperty("textureSingle");
|
Texture textureSingle = (Texture) wiz.getProperty("textureSingle");
|
||||||
Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
|
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");
|
boolean flipY = (Boolean) wiz.getProperty("flipY");
|
||||||
// reload the texture so we can use flipY
|
// reload the texture so we can use flipY
|
||||||
TextureKey key = (TextureKey) textureSingle.getKey();
|
TextureKey key = (TextureKey) textureSingle.getKey();
|
||||||
TextureKey newKey = new TextureKey(key.getName(), flipY);
|
TextureKey newKey = new TextureKey(key.getName(), flipY);
|
||||||
newKey.setGenerateMips(true);
|
newKey.setGenerateMips(true);
|
||||||
newKey.setAsCube(!useSpheremap);
|
if(type == SkyFactory.EnvMapType.CubeMap){
|
||||||
return SkyFactory.createSky(pm, pm.loadTexture(newKey), normalScale, useSpheremap);
|
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.multipleTexTopLoadButton.text=Load
|
||||||
SkyboxVisualPanel2.multipleTexBottomLoadButton.text=Load
|
SkyboxVisualPanel2.multipleTexBottomLoadButton.text=Load
|
||||||
SkyboxVisualPanel2.jLabel9.text=Normal Scale (x,y,z):
|
SkyboxVisualPanel2.jLabel9.text=Normal Scale (x,y,z):
|
||||||
SkyboxVisualPanel2.spheremapCheckBox.text=Sphere map
|
|
||||||
SkyboxVisualPanel2.normal1X.text=1
|
SkyboxVisualPanel2.normal1X.text=1
|
||||||
SkyboxVisualPanel2.normal1Y.text=1
|
SkyboxVisualPanel2.normal1Y.text=1
|
||||||
SkyboxVisualPanel2.normal1Z.text=1
|
SkyboxVisualPanel2.normal1Z.text=1
|
||||||
@ -32,3 +31,4 @@ SkyboxVisualPanel2.topPic.text=
|
|||||||
SkyboxVisualPanel2.bottomPic.text=
|
SkyboxVisualPanel2.bottomPic.text=
|
||||||
SkyboxVisualPanel2.singlePic.text=
|
SkyboxVisualPanel2.singlePic.text=
|
||||||
SkyboxVisualPanel2.flipYcheckBox.text=Flip Y
|
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">
|
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
@ -127,7 +127,7 @@
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace pref="27" max="32767" attributes="0"/>
|
<EmptySpace pref="29" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -172,7 +172,7 @@
|
|||||||
<Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="multipleTexTopLoadButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="multipleTexTopLoadButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="westPic" min="-2" pref="20" max="-2" attributes="0"/>
|
<Component id="westPic" min="-2" pref="20" max="-2" attributes="0"/>
|
||||||
@ -376,11 +376,17 @@
|
|||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" 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="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">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="jLabel8" min="-2" max="-2" attributes="0"/>
|
<Component id="jLabel8" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
|
||||||
<Component id="singleTexLoadButton" min="-2" 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"/>
|
<Component id="singlePic" min="-2" pref="20" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
|
||||||
<Component id="jLabel9" min="-2" 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"/>
|
<EmptySpace min="6" pref="6" max="-2" attributes="0"/>
|
||||||
<Component id="normal2Z" min="-2" pref="21" max="-2" attributes="1"/>
|
<Component id="normal2Z" min="-2" pref="21" max="-2" attributes="1"/>
|
||||||
</Group>
|
</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>
|
||||||
</Group>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
<EmptySpace pref="31" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -420,7 +422,10 @@
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
<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"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="flipYcheckBox" min="-2" max="-2" attributes="0"/>
|
<Component id="flipYcheckBox" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace pref="75" max="32767" attributes="0"/>
|
<EmptySpace pref="75" max="32767" attributes="0"/>
|
||||||
@ -474,13 +479,6 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JCheckBox" name="spheremapCheckBox">
|
|
||||||
<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, "{key}")"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
</Component>
|
|
||||||
<Component class="javax.swing.JLabel" name="singlePic">
|
<Component class="javax.swing.JLabel" name="singlePic">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
@ -495,6 +493,28 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<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.jLabel10.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -33,10 +33,12 @@ package com.jme3.gde.terraineditor.sky;
|
|||||||
|
|
||||||
import com.jme3.gde.core.assets.ProjectAssetManager;
|
import com.jme3.gde.core.assets.ProjectAssetManager;
|
||||||
import com.jme3.gde.core.properties.TexturePropertyEditor;
|
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.gde.core.scene.SceneApplication;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
|
import com.jme3.util.SkyFactory;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@ -46,11 +48,17 @@ import org.openide.util.ImageUtilities;
|
|||||||
|
|
||||||
public final class SkyboxVisualPanel2 extends JPanel {
|
public final class SkyboxVisualPanel2 extends JPanel {
|
||||||
|
|
||||||
private DDSPreview ddsPreview;
|
private TexturePreview texPreview;
|
||||||
|
|
||||||
/** Creates new form SkyboxVisualPanel2 */
|
/** Creates new form SkyboxVisualPanel2 */
|
||||||
public SkyboxVisualPanel2() {
|
public SkyboxVisualPanel2() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
|
DefaultComboBoxModel<SkyFactory.EnvMapType> model = new DefaultComboBoxModel<SkyFactory.EnvMapType>();
|
||||||
|
for (SkyFactory.EnvMapType value : SkyFactory.EnvMapType.values()) {
|
||||||
|
model.addElement(value);
|
||||||
|
}
|
||||||
|
mapTypeCombo.setModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -101,6 +109,14 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
return editorWest;
|
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
|
/** This method is called from within the constructor to
|
||||||
* initialize the form.
|
* initialize the form.
|
||||||
* WARNING: Do NOT modify this code. The content of this method is
|
* 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();
|
normal2X = new javax.swing.JTextField();
|
||||||
normal2Y = new javax.swing.JTextField();
|
normal2Y = new javax.swing.JTextField();
|
||||||
normal2Z = new javax.swing.JTextField();
|
normal2Z = new javax.swing.JTextField();
|
||||||
spheremapCheckBox = new javax.swing.JCheckBox();
|
|
||||||
singlePic = new javax.swing.JLabel();
|
singlePic = new javax.swing.JLabel();
|
||||||
flipYcheckBox = new javax.swing.JCheckBox();
|
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(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
|
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)
|
.addComponent(multipleTexTopLoadButton)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(topPic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)))))
|
.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.setVerticalGroup(
|
||||||
multipleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
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)
|
.addGroup(multipleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
.addComponent(jLabel5)
|
.addComponent(jLabel5)
|
||||||
.addComponent(multipleTexTopLoadButton))
|
.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()
|
.addGroup(multipleTexturePanelLayout.createSequentialGroup()
|
||||||
.addComponent(westPic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.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)
|
.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
|
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(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
|
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);
|
javax.swing.GroupLayout singleTexturePanelLayout = new javax.swing.GroupLayout(singleTexturePanel);
|
||||||
singleTexturePanel.setLayout(singleTexturePanelLayout);
|
singleTexturePanel.setLayout(singleTexturePanelLayout);
|
||||||
singleTexturePanelLayout.setHorizontalGroup(
|
singleTexturePanelLayout.setHorizontalGroup(
|
||||||
@ -359,11 +383,16 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
.addGroup(singleTexturePanelLayout.createSequentialGroup()
|
.addGroup(singleTexturePanelLayout.createSequentialGroup()
|
||||||
.addContainerGap()
|
.addContainerGap()
|
||||||
.addGroup(singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.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(jLabel10)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(mapTypeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 166, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
.addGroup(singleTexturePanelLayout.createSequentialGroup()
|
.addGroup(singleTexturePanelLayout.createSequentialGroup()
|
||||||
.addComponent(jLabel8)
|
.addComponent(jLabel8)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addGap(21, 21, 21)
|
||||||
.addComponent(singleTexLoadButton)
|
.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)
|
.addComponent(singlePic, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(39, 39, 39)
|
.addGap(39, 39, 39)
|
||||||
.addComponent(jLabel9)
|
.addComponent(jLabel9)
|
||||||
@ -372,11 +401,8 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(normal2Y, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(normal2Y, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(6, 6, 6)
|
.addGap(6, 6, 6)
|
||||||
.addComponent(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE))
|
.addComponent(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
.addGroup(singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
.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))
|
|
||||||
);
|
);
|
||||||
singleTexturePanelLayout.setVerticalGroup(
|
singleTexturePanelLayout.setVerticalGroup(
|
||||||
singleTexturePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
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(normal2Z, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(jLabel9))))
|
.addComponent(jLabel9))))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
.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)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(flipYcheckBox)
|
.addComponent(flipYcheckBox)
|
||||||
.addContainerGap(75, Short.MAX_VALUE))
|
.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
|
private void multipleTexSouthLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexSouthLoadButtonActionPerformed
|
||||||
Component view = editorSouth.getCustomEditor();
|
Component view = editorSouth.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorSouth.getValue() != null) {
|
if (editorSouth.getAsText()!= null) {
|
||||||
Texture tex = (Texture) editorSouth.getValue();
|
String selected = editorSouth.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, southPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexSouthLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexSouthLoadButtonActionPerformed
|
||||||
|
|
||||||
private void multipleTexNorthLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexNorthLoadButtonActionPerformed
|
private void multipleTexNorthLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexNorthLoadButtonActionPerformed
|
||||||
Component view = editorNorth.getCustomEditor();
|
Component view = editorNorth.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorNorth.getValue() != null) {
|
if (editorNorth.getAsText() != null) {
|
||||||
Texture tex = (Texture) editorNorth.getValue();
|
String selected = editorNorth.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, northPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexNorthLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexNorthLoadButtonActionPerformed
|
||||||
|
|
||||||
private void multipleTexEastLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexEastLoadButtonActionPerformed
|
private void multipleTexEastLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexEastLoadButtonActionPerformed
|
||||||
Component view = editorEast.getCustomEditor();
|
Component view = editorEast.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorEast.getValue() != null) {
|
if (editorEast.getAsText() != null) {
|
||||||
Texture tex = (Texture) editorEast.getValue();
|
String selected = editorEast.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, eastPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexEastLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexEastLoadButtonActionPerformed
|
||||||
|
|
||||||
private void multipleTexWestLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexWestLoadButtonActionPerformed
|
private void multipleTexWestLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexWestLoadButtonActionPerformed
|
||||||
Component view = editorWest.getCustomEditor();
|
Component view = editorWest.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorWest.getValue() != null) {
|
if (editorWest.getAsText() != null) {
|
||||||
Texture tex = (Texture) editorWest.getValue();
|
String selected = editorWest.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, westPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexWestLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexWestLoadButtonActionPerformed
|
||||||
|
|
||||||
private void multipleTexTopLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexTopLoadButtonActionPerformed
|
private void multipleTexTopLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexTopLoadButtonActionPerformed
|
||||||
Component view = editorTop.getCustomEditor();
|
Component view = editorTop.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorTop.getValue() != null) {
|
if (editorTop.getAsText() != null) {
|
||||||
Texture tex = (Texture) editorTop.getValue();
|
String selected = editorTop.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, topPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexTopLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexTopLoadButtonActionPerformed
|
||||||
|
|
||||||
private void multipleTexBottomLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexBottomLoadButtonActionPerformed
|
private void multipleTexBottomLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_multipleTexBottomLoadButtonActionPerformed
|
||||||
Component view = editorBottom.getCustomEditor();
|
Component view = editorBottom.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorBottom.getValue() != null) {
|
if (editorBottom.getAsText() != null) {
|
||||||
Texture tex = (Texture) editorBottom.getValue();
|
String selected = editorBottom.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, bottomPic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_multipleTexBottomLoadButtonActionPerformed
|
}//GEN-LAST:event_multipleTexBottomLoadButtonActionPerformed
|
||||||
|
|
||||||
private void singleTexLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_singleTexLoadButtonActionPerformed
|
private void singleTexLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_singleTexLoadButtonActionPerformed
|
||||||
Component view = editorSingle.getCustomEditor();
|
Component view = editorSingle.getCustomEditor();
|
||||||
view.setVisible(true);
|
view.setVisible(true);
|
||||||
if (editorSingle.getValue() != null) {
|
if (editorSingle.getAsText()!= null) {
|
||||||
Texture tex = (Texture) editorSingle.getValue();
|
String selected = editorSingle.getAsText();
|
||||||
String selected = tex.getKey().getName();
|
getTexturePreview().requestPreview(selected, "", 80, 80, singlePic, null);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_singleTexLoadButtonActionPerformed
|
}//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
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JLabel bottomPic;
|
private javax.swing.JLabel bottomPic;
|
||||||
private javax.swing.JLabel eastPic;
|
private javax.swing.JLabel eastPic;
|
||||||
private javax.swing.JCheckBox flipYcheckBox;
|
private javax.swing.JCheckBox flipYcheckBox;
|
||||||
private javax.swing.JLabel jLabel1;
|
private javax.swing.JLabel jLabel1;
|
||||||
|
private javax.swing.JLabel jLabel10;
|
||||||
private javax.swing.JLabel jLabel2;
|
private javax.swing.JLabel jLabel2;
|
||||||
private javax.swing.JLabel jLabel3;
|
private javax.swing.JLabel jLabel3;
|
||||||
private javax.swing.JLabel jLabel4;
|
private javax.swing.JLabel jLabel4;
|
||||||
@ -578,6 +534,7 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
private javax.swing.JLabel jLabel7;
|
private javax.swing.JLabel jLabel7;
|
||||||
private javax.swing.JLabel jLabel8;
|
private javax.swing.JLabel jLabel8;
|
||||||
private javax.swing.JLabel jLabel9;
|
private javax.swing.JLabel jLabel9;
|
||||||
|
private javax.swing.JComboBox mapTypeCombo;
|
||||||
private javax.swing.JButton multipleTexBottomLoadButton;
|
private javax.swing.JButton multipleTexBottomLoadButton;
|
||||||
private javax.swing.JButton multipleTexEastLoadButton;
|
private javax.swing.JButton multipleTexEastLoadButton;
|
||||||
private javax.swing.JButton multipleTexNorthLoadButton;
|
private javax.swing.JButton multipleTexNorthLoadButton;
|
||||||
@ -596,7 +553,6 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
private javax.swing.JButton singleTexLoadButton;
|
private javax.swing.JButton singleTexLoadButton;
|
||||||
private javax.swing.JPanel singleTexturePanel;
|
private javax.swing.JPanel singleTexturePanel;
|
||||||
private javax.swing.JLabel southPic;
|
private javax.swing.JLabel southPic;
|
||||||
private javax.swing.JCheckBox spheremapCheckBox;
|
|
||||||
private javax.swing.JLabel titleLabel;
|
private javax.swing.JLabel titleLabel;
|
||||||
private javax.swing.JLabel topPic;
|
private javax.swing.JLabel topPic;
|
||||||
private javax.swing.JLabel westPic;
|
private javax.swing.JLabel westPic;
|
||||||
@ -626,10 +582,11 @@ public final class SkyboxVisualPanel2 extends JPanel {
|
|||||||
return normal2Z;
|
return normal2Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JCheckBox getSpheremapCheckBox() {
|
public SkyFactory.EnvMapType getEnvMapType(){
|
||||||
return spheremapCheckBox;
|
return (SkyFactory.EnvMapType)mapTypeCombo.getSelectedItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public JCheckBox getFlipYCheckBox() {
|
public JCheckBox getFlipYCheckBox() {
|
||||||
return flipYcheckBox;
|
return flipYcheckBox;
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void storeSettings(Object settings) {
|
public void storeSettings(Object settings) {
|
||||||
WizardDescriptor wiz = (WizardDescriptor) settings;
|
WizardDescriptor wiz = (WizardDescriptor) settings;
|
||||||
SkyboxVisualPanel2 comp = (SkyboxVisualPanel2) getComponent();
|
SkyboxVisualPanel2 comp = (SkyboxVisualPanel2) getComponent();
|
||||||
@ -143,7 +144,7 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel {
|
|||||||
float y = new Float(comp.getNormal2Y().getText());
|
float y = new Float(comp.getNormal2Y().getText());
|
||||||
float z = new Float(comp.getNormal2Z().getText());
|
float z = new Float(comp.getNormal2Z().getText());
|
||||||
wiz.putProperty("normalScale", new Vector3f(x,y,z) );
|
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());
|
wiz.putProperty("flipY", comp.getFlipYCheckBox().isSelected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user