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.file.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 blenderContext * the blender context */ public ParticlesHelper(String blenderVersion, BlenderContext blenderContext) { super(blenderVersion, blenderContext); } @SuppressWarnings("unchecked") public ParticleEmitter toParticleEmitter(Structure particleSystem) throws BlenderFileException { ParticleEmitter result = null; Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part"); if (pParticleSettings.isNotNull()) { Structure particleSettings = pParticleSettings.fetchData().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; } }