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 acc = (DynamicArray) 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; } }