*/\r
package com.jme3.scene.plugins.blender.helpers.v249;\r
\r
+import java.nio.ByteBuffer;\r
import java.util.HashMap;\r
import java.util.List;\r
import java.util.Map;\r
import java.util.logging.Logger;\r
\r
import com.jme3.asset.BlenderKey.FeaturesToLoad;\r
+import com.jme3.asset.TextureKey;\r
import com.jme3.material.MatParam;\r
import com.jme3.material.Material;\r
import com.jme3.material.Material.MatParamTexture;\r
import com.jme3.material.RenderState.BlendMode;\r
import com.jme3.material.RenderState.FaceCullMode;\r
import com.jme3.math.ColorRGBA;\r
+import com.jme3.math.FastMath;\r
import com.jme3.scene.plugins.blender.data.Structure;\r
import com.jme3.scene.plugins.blender.exception.BlenderFileException;\r
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;\r
import com.jme3.scene.plugins.blender.utils.DynamicArray;\r
import com.jme3.scene.plugins.blender.utils.Pointer;\r
import com.jme3.shader.VarType;\r
+import com.jme3.texture.Image;\r
+import com.jme3.texture.Image.Format;\r
import com.jme3.texture.Texture;\r
import com.jme3.texture.Texture.WrapMode;\r
+import com.jme3.util.BufferUtils;\r
\r
public class MaterialHelper extends AbstractBlenderHelper {\r
private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());\r
public static final String TEXTURE_TYPE_GLOW = "GlowMap";\r
public static final String TEXTURE_TYPE_ALPHA = "AlphaMap";\r
\r
+ public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0);\r
+ public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1);\r
+ public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2);\r
+ public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3);\r
+ protected final Map<Integer, IAlphaMask> alphaMasks = new HashMap<Integer, IAlphaMask>();\r
+ \r
/**\r
* The type of the material's diffuse shader.\r
*/\r
*/\r
public MaterialHelper(String blenderVersion) {\r
super(blenderVersion);\r
+ //setting alpha masks\r
+ alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {\r
+ @Override\r
+ public void setImageSize(int width, int height) {}\r
+ \r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ return (byte)255;\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {\r
+ private float r;\r
+ private float[] center;\r
+ \r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[] {width*0.5f, height * 0.5f};\r
+ }\r
+ \r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1])));\r
+ return (byte)(d>=r ? 0 : 255);\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() {\r
+ private float r;\r
+ private float[] center;\r
+ \r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[] {width*0.5f, height * 0.5f};\r
+ }\r
+ \r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1])));\r
+ return (byte)(d>=r ? 0 : -255.0f*d/r+255.0f);\r
+ }\r
+ });\r
+ alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() {\r
+ private float r;\r
+ private float[] center;\r
+ \r
+ @Override\r
+ public void setImageSize(int width, int height) {\r
+ r = Math.min(width, height) * 0.5f;\r
+ center = new float[] {width*0.5f, height * 0.5f};\r
+ }\r
+ \r
+ @Override\r
+ public byte getAlpha(float x, float y) {\r
+ float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1]))) / r;\r
+ return d>=1.0f ? 0 : (byte)((-FastMath.sqrt((2.0f-d)*d)+1.0f)*255.0f);\r
+ }\r
+ });\r
}\r
\r
/**\r
* @param dataRepository the data repository\r
* @return material converted into particles-usable material\r
*/\r
- public Material getParticlesMaterial(Material material, DataRepository dataRepository) {\r
+ public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) {\r
Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");\r
\r
//copying texture\r
MatParam diffuseMap = material.getParam("DiffuseMap");\r
if(diffuseMap!=null) {\r
- Texture texture = (Texture) diffuseMap.getValue();\r
+ Texture texture = ((Texture) diffuseMap.getValue()).clone();\r
+ \r
+ //applying alpha mask to the texture\r
+ Image image = texture.getImage();\r
+ ByteBuffer sourceBB = image.getData(0);\r
+ sourceBB.rewind();\r
+ int w = image.getWidth();\r
+ int h = image.getHeight();\r
+ ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4);\r
+ IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex);\r
+ iAlphaMask.setImageSize(w, h);\r
+ \r
+ for(int x=0;x<w;++x) {\r
+ for(int y=0;y<h;++y) {\r
+ bb.put(sourceBB.get());\r
+ bb.put(sourceBB.get());\r
+ bb.put(sourceBB.get());\r
+ bb.put(iAlphaMask.getAlpha(x, y));\r
+ }\r
+ }\r
+\r
+ image = new Image(Format.RGBA8, w, h, bb);\r
+ texture.setImage(image);\r
+ \r
result.setTextureParam("Texture", VarType.Texture2D, texture);\r
}\r
\r
ColorRGBA color = (ColorRGBA) glowColor.getValue();\r
result.setParam("GlowColor", VarType.Vector3, color);\r
}\r
- //material.setTexture("Texture", dataRepository.getAssetManager().loadTexture("Effects/Explosion/flame.png"));\r
return result;\r
}\r
+ \r
+ protected byte calculateAlpha(float x, float y) {\r
+ return (byte)255;\r
+ }\r
+ \r
+ protected Texture loadParticleAlphaMapTexture(DataRepository dataRepository) {\r
+ TextureKey textureKey = new TextureKey(this.getClass().getPackage().getName().replace('.', '/') + "/particle_alpha_map.png");\r
+ return dataRepository.getAssetManager().loadTexture(textureKey);\r
+ }\r
\r
/**\r
* This method indicates if the material has a texture of a specified type.\r
}\r
}\r
}\r
+ \r
+ protected static interface IAlphaMask {\r
+ void setImageSize(int width, int height);\r
+ byte getAlpha(float x, float y);\r
+ }\r
}\r
\r
// creating vertices indices for this mesh\r
List<Integer> indexList = meshEntry.getValue();\r
- int[] indices = new int[indexList.size()];\r
- for (int i = 0; i < indexList.size(); ++i) {\r
- indices[i] = indexList.get(i).intValue();\r
+ short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices\r
+ for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer\r
+ indices[i] = indexList.get(i).shortValue();\r
}\r
\r
// setting vertices\r
- mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indices));\r
+ mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices));\r
mesh.setBuffer(verticesBuffer);\r
mesh.setBuffer(verticesBind);\r
\r
}\r
}\r
\r
+ /**\r
+ * This method applies particles emitter to the given node.\r
+ * @param node the particles emitter node\r
+ * @param modifier the modifier containing the emitter data\r
+ * @param dataRepository the data repository\r
+ * @return node with particles' emitter applied\r
+ */\r
protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {\r
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);\r
ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();\r
emitter = emitter.clone();\r
\r
+ //veryfying the alpha function for particles' texture\r
+ Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;\r
+ char nameSuffix = emitter.getName().charAt(emitter.getName().length()-1);\r
+ if(nameSuffix=='B' || nameSuffix=='N') {\r
+ alphaFunction = MaterialHelper.ALPHA_MASK_NONE;\r
+ }\r
+ //removing the type suffix from the name\r
+ emitter.setName(emitter.getName().substring(0, emitter.getName().length()-1));\r
+ \r
//applying emitter shape\r
EmitterShape emitterShape = emitter.getShape();\r
- if(emitterShape instanceof EmitterMeshVertexShape) {\r
- List<Mesh> meshes = new ArrayList<Mesh>();\r
- for(Spatial spatial : node.getChildren()) {\r
- if(spatial instanceof Geometry) {\r
- Mesh mesh = ((Geometry) spatial).getMesh();\r
- if(mesh != null) {\r
- meshes.add(mesh);\r
- Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), dataRepository);\r
- emitter.setMaterial(material);//TODO: rozbić na kilka części\r
- }\r
+ List<Mesh> meshes = new ArrayList<Mesh>();\r
+ for(Spatial spatial : node.getChildren()) {\r
+ if(spatial instanceof Geometry) {\r
+ Mesh mesh = ((Geometry) spatial).getMesh();\r
+ if(mesh != null) {\r
+ meshes.add(mesh);\r
+ Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);\r
+ emitter.setMaterial(material);//TODO: divide into several pieces\r
}\r
}\r
- if(meshes.size()>0) {\r
- ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);\r
- }\r
+ }\r
+ if(meshes.size()>0 && emitterShape instanceof EmitterMeshVertexShape) {\r
+ ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);\r
}\r
\r
node.attachChild(emitter);\r
import com.jme3.effect.EmitterMeshVertexShape;\r
import com.jme3.effect.ParticleEmitter;\r
import com.jme3.effect.ParticleMesh.Type;\r
+import com.jme3.effect.influencers.EmptyParticleInfluencer;\r
+import com.jme3.effect.influencers.ParticleInfluencer;\r
+import com.jme3.effect.influencers.NewtonianParticleInfluencer;\r
import com.jme3.math.ColorRGBA;\r
-import com.jme3.math.Vector3f;\r
import com.jme3.scene.plugins.blender.data.Structure;\r
import com.jme3.scene.plugins.blender.exception.BlenderFileException;\r
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;\r
public static final int PART_FROM_PARTICLE =3;\r
public static final int PART_FROM_CHILD =4;\r
\r
+ // part->phystype\r
+ public static final int PART_PHYS_NO = 0;\r
+ public static final int PART_PHYS_NEWTON= 1;\r
+ public static final int PART_PHYS_KEYED = 2;\r
+ public static final int PART_PHYS_BOIDS = 3;\r
+ \r
+ // part->draw_as\r
+ public static final int PART_DRAW_NOT = 0;\r
+ public static final int PART_DRAW_DOT = 1;\r
+ public static final int PART_DRAW_CIRC = 2;\r
+ public static final int PART_DRAW_CROSS = 3;\r
+ public static final int PART_DRAW_AXIS = 4;\r
+ public static final int PART_DRAW_LINE = 5;\r
+ public static final int PART_DRAW_PATH = 6;\r
+ public static final int PART_DRAW_OB = 7;\r
+ public static final int PART_DRAW_GR = 8;\r
+ public static final int PART_DRAW_BB = 9;\r
+ \r
/**\r
* This constructor parses the given blender version and stores the result. Some functionalities may differ in\r
* different blender versions.\r
Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");\r
if(!pParticleSettings.isNull()) {\r
Structure particleSettings = pParticleSettings.fetchData(dataRepository.getInputStream()).get(0);\r
+ \r
int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();\r
- result = new ParticleEmitter(particleSettings.getName(), Type.Triangle, totPart);\r
+ \r
+ //draw type will be stored temporarily in the name (it is used during modifier applying operation)\r
+ int drawAs = ((Number)particleSettings.getFieldValue("draw_as")).intValue();\r
+ char nameSuffix;//P - point, L - line, N - None, B - Bilboard\r
+ switch(drawAs) {\r
+ case PART_DRAW_NOT:\r
+ nameSuffix = 'N';\r
+ totPart = 0;//no need to generate particles in this case\r
+ break;\r
+ case PART_DRAW_BB:\r
+ nameSuffix = 'B';\r
+ break;\r
+ case PART_DRAW_OB:\r
+ case PART_DRAW_GR:\r
+ nameSuffix = 'P';\r
+ LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");//TODO: support groups and aobjects\r
+ break;\r
+ case PART_DRAW_LINE:\r
+ nameSuffix = 'L';\r
+ LOGGER.warning("Lines not yet supported! Using point representation instead!");//TODO: support lines\r
+ default://all others are rendered as points in blender\r
+ nameSuffix = 'P';\r
+ }\r
+ result = new ParticleEmitter(particleSettings.getName()+nameSuffix, Type.Triangle, totPart);\r
+ if(nameSuffix=='N') {\r
+ return result;//no need to set anything else\r
+ }\r
\r
//setting the emitters shape (the shapes meshes will be set later during modifier applying operation)\r
int from = ((Number)particleSettings.getFieldValue("from")).intValue();\r
result.setShape(new EmitterMeshConvexHullShape());\r
break;\r
default:\r
- LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter): " + from);\r
+ LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');\r
}\r
\r
//reading acceleration\r
DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");\r
- result.setInitialVelocity(new Vector3f(acc.get(0).floatValue(), acc.get(1).floatValue(), acc.get(2).floatValue()));\r
- result.setGravity(0);//by default gravity is set to 0.1f so we need to disable it completely\r
- // 2x2 texture animation\r
- result.setImagesX(2);\r
- result.setImagesY(2);\r
- result.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red\r
- result.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow\r
- result.setStartSize(1.5f);\r
- result.setEndSize(0.1f);\r
+ result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());\r
+ \r
+ //setting the colors\r
+ result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));\r
+ result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));\r
+ \r
+ //reading size\r
+ float sizeFactor = nameSuffix=='B' ? 1.0f : 0.3f;\r
+ float size = ((Number)particleSettings.getFieldValue("size")).floatValue() * sizeFactor;\r
+ result.setStartSize(size);\r
+ result.setEndSize(size);\r
\r
- result.setLowLife(0.5f);\r
- result.setHighLife(3f);\r
- result.setVelocityVariation(0.3f);\r
+ //reading lifetime\r
+ int fps = dataRepository.getBlenderKey().getFps();\r
+ float lifetime = ((Number)particleSettings.getFieldValue("lifetime")).floatValue() / fps;\r
+ float randlife = ((Number)particleSettings.getFieldValue("randlife")).floatValue() / fps;\r
+ result.setLowLife(lifetime * (1.0f - randlife));\r
+ result.setHighLife(lifetime);\r
+ \r
+ //preparing influencer\r
+ ParticleInfluencer influencer;\r
+ int phystype = ((Number)particleSettings.getFieldValue("phystype")).intValue();\r
+ switch(phystype) {\r
+ case PART_PHYS_NEWTON:\r
+ influencer = new NewtonianParticleInfluencer();\r
+ ((NewtonianParticleInfluencer)influencer).setNormalVelocity(((Number)particleSettings.getFieldValue("normfac")).floatValue());\r
+ ((NewtonianParticleInfluencer)influencer).setVelocityVariation(((Number)particleSettings.getFieldValue("randfac")).floatValue());\r
+ ((NewtonianParticleInfluencer)influencer).setSurfaceTangentFactor(((Number)particleSettings.getFieldValue("tanfac")).floatValue());\r
+ ((NewtonianParticleInfluencer)influencer).setSurfaceTangentRotation(((Number)particleSettings.getFieldValue("tanphase")).floatValue());\r
+ break;\r
+ case PART_PHYS_BOIDS:\r
+ case PART_PHYS_KEYED://TODO: support other influencers\r
+ LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");\r
+ case PART_PHYS_NO:\r
+ default:\r
+ influencer = new EmptyParticleInfluencer();\r
+ }\r
+ result.setParticleInfluencer(influencer);\r
}\r
return result;\r
}\r