package com.jme3.scene.plugins.blender.particles; | |
import com.jme3.effect.ParticleEmitter; | |
import com.jme3.effect.ParticleMesh.Type; | |
import com.jme3.effect.influencers.EmptyParticleInfluencer; | |
import com.jme3.effect.influencers.NewtonianParticleInfluencer; | |
import com.jme3.effect.influencers.ParticleInfluencer; | |
import com.jme3.effect.shapes.EmitterMeshConvexHullShape; | |
import com.jme3.effect.shapes.EmitterMeshFaceShape; | |
import com.jme3.effect.shapes.EmitterMeshVertexShape; | |
import com.jme3.math.ColorRGBA; | |
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; | |
import com.jme3.scene.plugins.blender.BlenderContext; | |
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; | |
import com.jme3.scene.plugins.blender.file.DynamicArray; | |
import com.jme3.scene.plugins.blender.file.Pointer; | |
import com.jme3.scene.plugins.blender.file.Structure; | |
import java.util.logging.Logger; | |
public class ParticlesHelper extends AbstractBlenderHelper { | |
private static final Logger LOGGER = Logger.getLogger(ParticlesHelper.class.getName()); | |
// part->type | |
public static final int PART_EMITTER = 0; | |
public static final int PART_REACTOR = 1; | |
public static final int PART_HAIR = 2; | |
public static final int PART_FLUID = 3; | |
// part->flag | |
public static final int PART_REACT_STA_END =1; | |
public static final int PART_REACT_MULTIPLE =2; | |
public static final int PART_LOOP =4; | |
//public static final int PART_LOOP_INSTANT =8; | |
public static final int PART_HAIR_GEOMETRY =16; | |
public static final int PART_UNBORN =32; //show unborn particles | |
public static final int PART_DIED =64; //show died particles | |
public static final int PART_TRAND =128; | |
public static final int PART_EDISTR =256; // particle/face from face areas | |
public static final int PART_STICKY =512; //collided particles can stick to collider | |
public static final int PART_DIE_ON_COL =1<<12; | |
public static final int PART_SIZE_DEFL =1<<13; // swept sphere deflections | |
public static final int PART_ROT_DYN =1<<14; // dynamic rotation | |
public static final int PART_SIZEMASS =1<<16; | |
public static final int PART_ABS_LENGTH =1<<15; | |
public static final int PART_ABS_TIME =1<<17; | |
public static final int PART_GLOB_TIME =1<<18; | |
public static final int PART_BOIDS_2D =1<<19; | |
public static final int PART_BRANCHING =1<<20; | |
public static final int PART_ANIM_BRANCHING =1<<21; | |
public static final int PART_SELF_EFFECT =1<<22; | |
public static final int PART_SYMM_BRANCHING =1<<24; | |
public static final int PART_HAIR_BSPLINE =1024; | |
public static final int PART_GRID_INVERT =1<<26; | |
public static final int PART_CHILD_EFFECT =1<<27; | |
public static final int PART_CHILD_SEAMS =1<<28; | |
public static final int PART_CHILD_RENDER =1<<29; | |
public static final int PART_CHILD_GUIDE =1<<30; | |
// part->from | |
public static final int PART_FROM_VERT =0; | |
public static final int PART_FROM_FACE =1; | |
public static final int PART_FROM_VOLUME =2; | |
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. | |
* @param blenderVersion | |
* the version read from the blend file | |
* @param fixUpAxis | |
* a variable that indicates if the Y asxis is the UP axis or not | |
*/ | |
public ParticlesHelper(String blenderVersion, boolean fixUpAxis) { | |
super(blenderVersion, fixUpAxis); | |
} | |
@SuppressWarnings("unchecked") | |
public ParticleEmitter toParticleEmitter(Structure particleSystem, BlenderContext blenderContext) throws BlenderFileException { | |
ParticleEmitter result = null; | |
Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part"); | |
if(pParticleSettings.isNotNull()) { | |
Structure particleSettings = pParticleSettings.fetchData(blenderContext.getInputStream()).get(0); | |
int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue(); | |
//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(); | |
switch(from) { | |
case PART_FROM_VERT: | |
result.setShape(new EmitterMeshVertexShape()); | |
break; | |
case PART_FROM_FACE: | |
result.setShape(new EmitterMeshFaceShape()); | |
break; | |
case PART_FROM_VOLUME: | |
result.setShape(new EmitterMeshConvexHullShape()); | |
break; | |
default: | |
LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')'); | |
} | |
//reading acceleration | |
DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc"); | |
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); | |
//reading lifetime | |
int fps = blenderContext.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; | |
} | |
@Override | |
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { | |
return true; | |
} | |
} |