1 package com.jme3.scene.plugins.blender.particles; 2 3 import com.jme3.effect.ParticleEmitter; 4 import com.jme3.effect.ParticleMesh.Type; 5 import com.jme3.effect.influencers.EmptyParticleInfluencer; 6 import com.jme3.effect.influencers.NewtonianParticleInfluencer; 7 import com.jme3.effect.influencers.ParticleInfluencer; 8 import com.jme3.effect.shapes.EmitterMeshConvexHullShape; 9 import com.jme3.effect.shapes.EmitterMeshFaceShape; 10 import com.jme3.effect.shapes.EmitterMeshVertexShape; 11 import com.jme3.math.ColorRGBA; 12 import com.jme3.scene.plugins.blender.AbstractBlenderHelper; 13 import com.jme3.scene.plugins.blender.BlenderContext; 14 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 15 import com.jme3.scene.plugins.blender.file.DynamicArray; 16 import com.jme3.scene.plugins.blender.file.Pointer; 17 import com.jme3.scene.plugins.blender.file.Structure; 18 import java.util.logging.Logger; 19 20 public class ParticlesHelper extends AbstractBlenderHelper { 21 private static final Logger LOGGER = Logger.getLogger(ParticlesHelper.class.getName()); 22 23 // part->type 24 public static final int PART_EMITTER = 0; 25 public static final int PART_REACTOR = 1; 26 public static final int PART_HAIR = 2; 27 public static final int PART_FLUID = 3; 28 29 // part->flag 30 public static final int PART_REACT_STA_END =1; 31 public static final int PART_REACT_MULTIPLE =2; 32 public static final int PART_LOOP =4; 33 //public static final int PART_LOOP_INSTANT =8; 34 public static final int PART_HAIR_GEOMETRY =16; 35 public static final int PART_UNBORN =32; //show unborn particles 36 public static final int PART_DIED =64; //show died particles 37 public static final int PART_TRAND =128; 38 public static final int PART_EDISTR =256; // particle/face from face areas 39 public static final int PART_STICKY =512; //collided particles can stick to collider 40 public static final int PART_DIE_ON_COL =1<<12; 41 public static final int PART_SIZE_DEFL =1<<13; // swept sphere deflections 42 public static final int PART_ROT_DYN =1<<14; // dynamic rotation 43 public static final int PART_SIZEMASS =1<<16; 44 public static final int PART_ABS_LENGTH =1<<15; 45 public static final int PART_ABS_TIME =1<<17; 46 public static final int PART_GLOB_TIME =1<<18; 47 public static final int PART_BOIDS_2D =1<<19; 48 public static final int PART_BRANCHING =1<<20; 49 public static final int PART_ANIM_BRANCHING =1<<21; 50 public static final int PART_SELF_EFFECT =1<<22; 51 public static final int PART_SYMM_BRANCHING =1<<24; 52 public static final int PART_HAIR_BSPLINE =1024; 53 public static final int PART_GRID_INVERT =1<<26; 54 public static final int PART_CHILD_EFFECT =1<<27; 55 public static final int PART_CHILD_SEAMS =1<<28; 56 public static final int PART_CHILD_RENDER =1<<29; 57 public static final int PART_CHILD_GUIDE =1<<30; 58 59 // part->from 60 public static final int PART_FROM_VERT =0; 61 public static final int PART_FROM_FACE =1; 62 public static final int PART_FROM_VOLUME =2; 63 public static final int PART_FROM_PARTICLE =3; 64 public static final int PART_FROM_CHILD =4; 65 66 // part->phystype 67 public static final int PART_PHYS_NO = 0; 68 public static final int PART_PHYS_NEWTON= 1; 69 public static final int PART_PHYS_KEYED = 2; 70 public static final int PART_PHYS_BOIDS = 3; 71 72 // part->draw_as 73 public static final int PART_DRAW_NOT = 0; 74 public static final int PART_DRAW_DOT = 1; 75 public static final int PART_DRAW_CIRC = 2; 76 public static final int PART_DRAW_CROSS = 3; 77 public static final int PART_DRAW_AXIS = 4; 78 public static final int PART_DRAW_LINE = 5; 79 public static final int PART_DRAW_PATH = 6; 80 public static final int PART_DRAW_OB = 7; 81 public static final int PART_DRAW_GR = 8; 82 public static final int PART_DRAW_BB = 9; 83 84 /** 85 * This constructor parses the given blender version and stores the result. Some functionalities may differ in 86 * different blender versions. 87 * @param blenderVersion 88 * the version read from the blend file 89 * @param fixUpAxis 90 * a variable that indicates if the Y asxis is the UP axis or not 91 */ 92 public ParticlesHelper(String blenderVersion, boolean fixUpAxis) { 93 super(blenderVersion, fixUpAxis); 94 } 95 96 @SuppressWarnings("unchecked") 97 public ParticleEmitter toParticleEmitter(Structure particleSystem, BlenderContext blenderContext) throws BlenderFileException { 98 ParticleEmitter result = null; 99 Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part"); 100 if(pParticleSettings.isNotNull()) { 101 Structure particleSettings = pParticleSettings.fetchData(blenderContext.getInputStream()).get(0); 102 103 int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue(); 104 105 //draw type will be stored temporarily in the name (it is used during modifier applying operation) 106 int drawAs = ((Number)particleSettings.getFieldValue("draw_as")).intValue(); 107 char nameSuffix;//P - point, L - line, N - None, B - Bilboard 108 switch(drawAs) { 109 case PART_DRAW_NOT: 110 nameSuffix = 'N'; 111 totPart = 0;//no need to generate particles in this case 112 break; 113 case PART_DRAW_BB: 114 nameSuffix = 'B'; 115 break; 116 case PART_DRAW_OB: 117 case PART_DRAW_GR: 118 nameSuffix = 'P'; 119 LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");//TODO: support groups and aobjects 120 break; 121 case PART_DRAW_LINE: 122 nameSuffix = 'L'; 123 LOGGER.warning("Lines not yet supported! Using point representation instead!");//TODO: support lines 124 default://all others are rendered as points in blender 125 nameSuffix = 'P'; 126 } 127 result = new ParticleEmitter(particleSettings.getName()+nameSuffix, Type.Triangle, totPart); 128 if(nameSuffix=='N') { 129 return result;//no need to set anything else 130 } 131 132 //setting the emitters shape (the shapes meshes will be set later during modifier applying operation) 133 int from = ((Number)particleSettings.getFieldValue("from")).intValue(); 134 switch(from) { 135 case PART_FROM_VERT: 136 result.setShape(new EmitterMeshVertexShape()); 137 break; 138 case PART_FROM_FACE: 139 result.setShape(new EmitterMeshFaceShape()); 140 break; 141 case PART_FROM_VOLUME: 142 result.setShape(new EmitterMeshConvexHullShape()); 143 break; 144 default: 145 LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')'); 146 } 147 148 //reading acceleration 149 DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc"); 150 result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue()); 151 152 //setting the colors 153 result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f)); 154 result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); 155 156 //reading size 157 float sizeFactor = nameSuffix=='B' ? 1.0f : 0.3f; 158 float size = ((Number)particleSettings.getFieldValue("size")).floatValue() * sizeFactor; 159 result.setStartSize(size); 160 result.setEndSize(size); 161 162 //reading lifetime 163 int fps = blenderContext.getBlenderKey().getFps(); 164 float lifetime = ((Number)particleSettings.getFieldValue("lifetime")).floatValue() / fps; 165 float randlife = ((Number)particleSettings.getFieldValue("randlife")).floatValue() / fps; 166 result.setLowLife(lifetime * (1.0f - randlife)); 167 result.setHighLife(lifetime); 168 169 //preparing influencer 170 ParticleInfluencer influencer; 171 int phystype = ((Number)particleSettings.getFieldValue("phystype")).intValue(); 172 switch(phystype) { 173 case PART_PHYS_NEWTON: 174 influencer = new NewtonianParticleInfluencer(); 175 ((NewtonianParticleInfluencer)influencer).setNormalVelocity(((Number)particleSettings.getFieldValue("normfac")).floatValue()); 176 ((NewtonianParticleInfluencer)influencer).setVelocityVariation(((Number)particleSettings.getFieldValue("randfac")).floatValue()); 177 ((NewtonianParticleInfluencer)influencer).setSurfaceTangentFactor(((Number)particleSettings.getFieldValue("tanfac")).floatValue()); 178 ((NewtonianParticleInfluencer)influencer).setSurfaceTangentRotation(((Number)particleSettings.getFieldValue("tanphase")).floatValue()); 179 break; 180 case PART_PHYS_BOIDS: 181 case PART_PHYS_KEYED://TODO: support other influencers 182 LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!"); 183 case PART_PHYS_NO: 184 default: 185 influencer = new EmptyParticleInfluencer(); 186 } 187 result.setParticleInfluencer(influencer); 188 } 189 return result; 190 } 191 192 @Override 193 public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { 194 return true; 195 } 196 } 197