Support for Newtonian Physics in particles importing.
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7565 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
49116be02d
commit
c6be5633ce
@ -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());
|
||||
|
||||
result.setLowLife(0.5f);
|
||||
result.setHighLife(3f);
|
||||
result.setVelocityVariation(0.3f);
|
||||
//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);
|
||||
|
||||
//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…
x
Reference in New Issue
Block a user