Support for Newtonian Physics in particles importing.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7565 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 14 years ago
parent 49116be02d
commit c6be5633ce
  1. 112
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MaterialHelper.java
  2. 8
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java
  3. 40
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java
  4. 102
      engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ParticlesHelper.java

@ -31,6 +31,7 @@
*/
package com.jme3.scene.plugins.blender.helpers.v249;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -39,12 +40,14 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.asset.TextureKey;
import com.jme3.material.MatParam;
import com.jme3.material.Material;
import com.jme3.material.Material.MatParamTexture;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.scene.plugins.blender.data.Structure;
import com.jme3.scene.plugins.blender.exception.BlenderFileException;
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;
@ -53,8 +56,11 @@ import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType
import com.jme3.scene.plugins.blender.utils.DynamicArray;
import com.jme3.scene.plugins.blender.utils.Pointer;
import com.jme3.shader.VarType;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.BufferUtils;
public class MaterialHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(MaterialHelper.class.getName());
@ -67,6 +73,12 @@ public class MaterialHelper extends AbstractBlenderHelper {
public static final String TEXTURE_TYPE_GLOW = "GlowMap";
public static final String TEXTURE_TYPE_ALPHA = "AlphaMap";
public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0);
public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1);
public static final Integer ALPHA_MASK_CONE = Integer.valueOf(2);
public static final Integer ALPHA_MASK_HYPERBOLE = Integer.valueOf(3);
protected final Map<Integer, IAlphaMask> alphaMasks = new HashMap<Integer, IAlphaMask>();
/**
* The type of the material's diffuse shader.
*/
@ -93,6 +105,64 @@ public class MaterialHelper extends AbstractBlenderHelper {
*/
public MaterialHelper(String blenderVersion) {
super(blenderVersion);
//setting alpha masks
alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {
@Override
public void setImageSize(int width, int height) {}
@Override
public byte getAlpha(float x, float y) {
return (byte)255;
}
});
alphaMasks.put(ALPHA_MASK_CIRCLE, new IAlphaMask() {
private float r;
private float[] center;
@Override
public void setImageSize(int width, int height) {
r = Math.min(width, height) * 0.5f;
center = new float[] {width*0.5f, height * 0.5f};
}
@Override
public byte getAlpha(float x, float y) {
float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1])));
return (byte)(d>=r ? 0 : 255);
}
});
alphaMasks.put(ALPHA_MASK_CONE, new IAlphaMask() {
private float r;
private float[] center;
@Override
public void setImageSize(int width, int height) {
r = Math.min(width, height) * 0.5f;
center = new float[] {width*0.5f, height * 0.5f};
}
@Override
public byte getAlpha(float x, float y) {
float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1])));
return (byte)(d>=r ? 0 : -255.0f*d/r+255.0f);
}
});
alphaMasks.put(ALPHA_MASK_HYPERBOLE, new IAlphaMask() {
private float r;
private float[] center;
@Override
public void setImageSize(int width, int height) {
r = Math.min(width, height) * 0.5f;
center = new float[] {width*0.5f, height * 0.5f};
}
@Override
public byte getAlpha(float x, float y) {
float d = FastMath.abs(FastMath.sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1]))) / r;
return d>=1.0f ? 0 : (byte)((-FastMath.sqrt((2.0f-d)*d)+1.0f)*255.0f);
}
});
}
/**
@ -287,13 +357,36 @@ public class MaterialHelper extends AbstractBlenderHelper {
* @param dataRepository the data repository
* @return material converted into particles-usable material
*/
public Material getParticlesMaterial(Material material, DataRepository dataRepository) {
public Material getParticlesMaterial(Material material, Integer alphaMaskIndex, DataRepository dataRepository) {
Material result = new Material(dataRepository.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
//copying texture
MatParam diffuseMap = material.getParam("DiffuseMap");
if(diffuseMap!=null) {
Texture texture = (Texture) diffuseMap.getValue();
Texture texture = ((Texture) diffuseMap.getValue()).clone();
//applying alpha mask to the texture
Image image = texture.getImage();
ByteBuffer sourceBB = image.getData(0);
sourceBB.rewind();
int w = image.getWidth();
int h = image.getHeight();
ByteBuffer bb = BufferUtils.createByteBuffer(w * h * 4);
IAlphaMask iAlphaMask = alphaMasks.get(alphaMaskIndex);
iAlphaMask.setImageSize(w, h);
for(int x=0;x<w;++x) {
for(int y=0;y<h;++y) {
bb.put(sourceBB.get());
bb.put(sourceBB.get());
bb.put(sourceBB.get());
bb.put(iAlphaMask.getAlpha(x, y));
}
}
image = new Image(Format.RGBA8, w, h, bb);
texture.setImage(image);
result.setTextureParam("Texture", VarType.Texture2D, texture);
}
@ -303,9 +396,17 @@ public class MaterialHelper extends AbstractBlenderHelper {
ColorRGBA color = (ColorRGBA) glowColor.getValue();
result.setParam("GlowColor", VarType.Vector3, color);
}
//material.setTexture("Texture", dataRepository.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
return result;
}
protected byte calculateAlpha(float x, float y) {
return (byte)255;
}
protected Texture loadParticleAlphaMapTexture(DataRepository dataRepository) {
TextureKey textureKey = new TextureKey(this.getClass().getPackage().getName().replace('.', '/') + "/particle_alpha_map.png");
return dataRepository.getAssetManager().loadTexture(textureKey);
}
/**
* This method indicates if the material has a texture of a specified type.
@ -586,4 +687,9 @@ public class MaterialHelper extends AbstractBlenderHelper {
}
}
}
protected static interface IAlphaMask {
void setImageSize(int width, int height);
byte getAlpha(float x, float y);
}
}

@ -301,13 +301,13 @@ public class MeshHelper extends AbstractBlenderHelper {
// creating vertices indices for this mesh
List<Integer> indexList = meshEntry.getValue();
int[] indices = new int[indexList.size()];
for (int i = 0; i < indexList.size(); ++i) {
indices[i] = indexList.get(i).intValue();
short[] indices = new short[indexList.size()];//TODO: check if the model doesn't have more than 32767 vertices
for (int i = 0; i < indexList.size(); ++i) {//if yes then mesh.getVertices method must be changed to accept other than ShortBuffer
indices[i] = indexList.get(i).shortValue();
}
// setting vertices
mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indices));
mesh.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indices));
mesh.setBuffer(verticesBuffer);
mesh.setBuffer(verticesBind);

@ -235,28 +235,42 @@ public class ModifierHelper extends AbstractBlenderHelper {
}
}
/**
* This method applies particles emitter to the given node.
* @param node the particles emitter node
* @param modifier the modifier containing the emitter data
* @param dataRepository the data repository
* @return node with particles' emitter applied
*/
protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();
emitter = emitter.clone();
//veryfying the alpha function for particles' texture
Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;
char nameSuffix = emitter.getName().charAt(emitter.getName().length()-1);
if(nameSuffix=='B' || nameSuffix=='N') {
alphaFunction = MaterialHelper.ALPHA_MASK_NONE;
}
//removing the type suffix from the name
emitter.setName(emitter.getName().substring(0, emitter.getName().length()-1));
//applying emitter shape
EmitterShape emitterShape = emitter.getShape();
if(emitterShape instanceof EmitterMeshVertexShape) {
List<Mesh> meshes = new ArrayList<Mesh>();
for(Spatial spatial : node.getChildren()) {
if(spatial instanceof Geometry) {
Mesh mesh = ((Geometry) spatial).getMesh();
if(mesh != null) {
meshes.add(mesh);
Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), dataRepository);
emitter.setMaterial(material);//TODO: rozbić na kilka części
}
List<Mesh> meshes = new ArrayList<Mesh>();
for(Spatial spatial : node.getChildren()) {
if(spatial instanceof Geometry) {
Mesh mesh = ((Geometry) spatial).getMesh();
if(mesh != null) {
meshes.add(mesh);
Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);
emitter.setMaterial(material);//TODO: divide into several pieces
}
}
if(meshes.size()>0) {
((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
}
}
if(meshes.size()>0 && emitterShape instanceof EmitterMeshVertexShape) {
((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
}
node.attachChild(emitter);

@ -7,8 +7,10 @@ import com.jme3.effect.EmitterMeshFaceShape;
import com.jme3.effect.EmitterMeshVertexShape;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.influencers.EmptyParticleInfluencer;
import com.jme3.effect.influencers.ParticleInfluencer;
import com.jme3.effect.influencers.NewtonianParticleInfluencer;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.data.Structure;
import com.jme3.scene.plugins.blender.exception.BlenderFileException;
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;
@ -62,6 +64,24 @@ public class ParticlesHelper extends AbstractBlenderHelper {
public static final int PART_FROM_PARTICLE =3;
public static final int PART_FROM_CHILD =4;
// part->phystype
public static final int PART_PHYS_NO = 0;
public static final int PART_PHYS_NEWTON= 1;
public static final int PART_PHYS_KEYED = 2;
public static final int PART_PHYS_BOIDS = 3;
// part->draw_as
public static final int PART_DRAW_NOT = 0;
public static final int PART_DRAW_DOT = 1;
public static final int PART_DRAW_CIRC = 2;
public static final int PART_DRAW_CROSS = 3;
public static final int PART_DRAW_AXIS = 4;
public static final int PART_DRAW_LINE = 5;
public static final int PART_DRAW_PATH = 6;
public static final int PART_DRAW_OB = 7;
public static final int PART_DRAW_GR = 8;
public static final int PART_DRAW_BB = 9;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
@ -78,8 +98,35 @@ public class ParticlesHelper extends AbstractBlenderHelper {
Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");
if(!pParticleSettings.isNull()) {
Structure particleSettings = pParticleSettings.fetchData(dataRepository.getInputStream()).get(0);
int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();
result = new ParticleEmitter(particleSettings.getName(), Type.Triangle, totPart);
//draw type will be stored temporarily in the name (it is used during modifier applying operation)
int drawAs = ((Number)particleSettings.getFieldValue("draw_as")).intValue();
char nameSuffix;//P - point, L - line, N - None, B - Bilboard
switch(drawAs) {
case PART_DRAW_NOT:
nameSuffix = 'N';
totPart = 0;//no need to generate particles in this case
break;
case PART_DRAW_BB:
nameSuffix = 'B';
break;
case PART_DRAW_OB:
case PART_DRAW_GR:
nameSuffix = 'P';
LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");//TODO: support groups and aobjects
break;
case PART_DRAW_LINE:
nameSuffix = 'L';
LOGGER.warning("Lines not yet supported! Using point representation instead!");//TODO: support lines
default://all others are rendered as points in blender
nameSuffix = 'P';
}
result = new ParticleEmitter(particleSettings.getName()+nameSuffix, Type.Triangle, totPart);
if(nameSuffix=='N') {
return result;//no need to set anything else
}
//setting the emitters shape (the shapes meshes will be set later during modifier applying operation)
int from = ((Number)particleSettings.getFieldValue("from")).intValue();
@ -94,24 +141,49 @@ public class ParticlesHelper extends AbstractBlenderHelper {
result.setShape(new EmitterMeshConvexHullShape());
break;
default:
LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter): " + from);
LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');
}
//reading acceleration
DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");
result.setInitialVelocity(new Vector3f(acc.get(0).floatValue(), acc.get(1).floatValue(), acc.get(2).floatValue()));
result.setGravity(0);//by default gravity is set to 0.1f so we need to disable it completely
// 2x2 texture animation
result.setImagesX(2);
result.setImagesY(2);
result.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red
result.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
result.setStartSize(1.5f);
result.setEndSize(0.1f);
result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());
//setting the colors
result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));
result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));
//reading size
float sizeFactor = nameSuffix=='B' ? 1.0f : 0.3f;
float size = ((Number)particleSettings.getFieldValue("size")).floatValue() * sizeFactor;
result.setStartSize(size);
result.setEndSize(size);
result.setLowLife(0.5f);
result.setHighLife(3f);
result.setVelocityVariation(0.3f);
//reading lifetime
int fps = dataRepository.getBlenderKey().getFps();
float lifetime = ((Number)particleSettings.getFieldValue("lifetime")).floatValue() / fps;
float randlife = ((Number)particleSettings.getFieldValue("randlife")).floatValue() / fps;
result.setLowLife(lifetime * (1.0f - randlife));
result.setHighLife(lifetime);
//preparing influencer
ParticleInfluencer influencer;
int phystype = ((Number)particleSettings.getFieldValue("phystype")).intValue();
switch(phystype) {
case PART_PHYS_NEWTON:
influencer = new NewtonianParticleInfluencer();
((NewtonianParticleInfluencer)influencer).setNormalVelocity(((Number)particleSettings.getFieldValue("normfac")).floatValue());
((NewtonianParticleInfluencer)influencer).setVelocityVariation(((Number)particleSettings.getFieldValue("randfac")).floatValue());
((NewtonianParticleInfluencer)influencer).setSurfaceTangentFactor(((Number)particleSettings.getFieldValue("tanfac")).floatValue());
((NewtonianParticleInfluencer)influencer).setSurfaceTangentRotation(((Number)particleSettings.getFieldValue("tanphase")).floatValue());
break;
case PART_PHYS_BOIDS:
case PART_PHYS_KEYED://TODO: support other influencers
LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");
case PART_PHYS_NO:
default:
influencer = new EmptyParticleInfluencer();
}
result.setParticleInfluencer(influencer);
}
return result;
}

Loading…
Cancel
Save