particle.cpp
Engine/source/T3D/fx/particle.cpp
Public Variables
Public Functions
ConsoleDocClass(ParticleData , "@brief Contains information <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> how specific particles should look and react " "including particle colors, particle imagemap, acceleration <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> individual " "particles and spin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">information.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classparticledata/">ParticleData</a>(GLWaterExpSmoke)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " textureName=\"art/shapes/particles/smoke\";\n" " dragCoefficient = 0.4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " gravityCoefficient = -0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " inheritedVelFactor = 0.025;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " constantAcceleration = -1.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetimeMS = 1250;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetimeVarianceMS = 0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " useInvAlpha = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinSpeed = 1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinRandomMin = -200.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinRandomMax = 200.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " colors[0] = \"0.1 0.1 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " colors[1] = \"0.4 0.4 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " colors[2] = \"0.4 0.4 1.0 0.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " sizes[0] = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " sizes[1] = 6.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " sizes[2] = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " times[0] = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " times[1] = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " times[2] = 1.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitter\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitterData\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitterNode\n</a>" )
DefineEngineMethod(ParticleData , reload , void , () )
dragCoefFValidator(0. f, 5. f)
gravCoefFValidator(-10. f, 10. f)
spinRandFValidator(-1000. f, 1000. f)
Detailed Description
Public Variables
const F32 sgDefaultConstantAcceleration
const F32 sgDefaultSizeBias
const F32 sgDefaultSpinBias
const F32 sgDefaultSpinRandomMax
const F32 sgDefaultSpinRandomMin
const F32 sgDefaultSpinSpeed
const F32 sgDefaultWindCoefficient
Public Functions
ConsoleDocClass(ParticleData , "@brief Contains information <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> how specific particles should look and react " "including particle colors, particle imagemap, acceleration <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> individual " "particles and spin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">information.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classparticledata/">ParticleData</a>(GLWaterExpSmoke)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " textureName=\"art/shapes/particles/smoke\";\n" " dragCoefficient = 0.4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " gravityCoefficient = -0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " inheritedVelFactor = 0.025;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " constantAcceleration = -1.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetimeMS = 1250;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetimeVarianceMS = 0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " useInvAlpha = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinSpeed = 1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinRandomMin = -200.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " spinRandomMax = 200.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " colors[0] = \"0.1 0.1 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " colors[1] = \"0.4 0.4 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " colors[2] = \"0.4 0.4 1.0 0.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " sizes[0] = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " sizes[1] = 6.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " sizes[2] = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " times[0] = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " times[1] = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " times[2] = 1.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitter\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitterData\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ParticleEmitterNode\n</a>" )
DefineEngineMethod(ParticleData , reload , void , () )
dragCoefFValidator(0. f, 5. f)
gravCoefFValidator(-10. f, 10. f)
IMPLEMENT_CO_DATABLOCK_V1(ParticleData )
spinRandFValidator(-1000. f, 1000. f)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames 26// Copyright (C) 2015 Faust Logic, Inc. 27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 28 29#include "particle.h" 30#include "console/consoleTypes.h" 31#include "console/typeValidators.h" 32#include "core/stream/bitStream.h" 33#include "math/mRandom.h" 34#include "math/mathIO.h" 35#include "console/engineAPI.h" 36 37IMPLEMENT_CO_DATABLOCK_V1( ParticleData ); 38 39ConsoleDocClass( ParticleData, 40 "@brief Contains information for how specific particles should look and react " 41 "including particle colors, particle imagemap, acceleration value for individual " 42 "particles and spin information.\n" 43 44 "@tsexample\n" 45 "datablock ParticleData( GLWaterExpSmoke )\n" 46 "{\n" 47 " textureName = \"art/shapes/particles/smoke\";\n" 48 " dragCoefficient = 0.4;\n" 49 " gravityCoefficient = -0.25;\n" 50 " inheritedVelFactor = 0.025;\n" 51 " constantAcceleration = -1.1;\n" 52 " lifetimeMS = 1250;\n" 53 " lifetimeVarianceMS = 0;\n" 54 " useInvAlpha = false;\n" 55 " spinSpeed = 1;\n" 56 " spinRandomMin = -200.0;\n" 57 " spinRandomMax = 200.0;\n\n" 58 " colors[0] = \"0.1 0.1 1.0 1.0\";\n" 59 " colors[1] = \"0.4 0.4 1.0 1.0\";\n" 60 " colors[2] = \"0.4 0.4 1.0 0.0\";\n\n" 61 " sizes[0] = 2.0;\n" 62 " sizes[1] = 6.0;\n" 63 " sizes[2] = 2.0;\n\n" 64 " times[0] = 0.0;\n" 65 " times[1] = 0.5;\n" 66 " times[2] = 1.0;\n" 67 "};\n" 68 "@endtsexample\n" 69 70 "@ingroup FX\n" 71 "@see ParticleEmitter\n" 72 "@see ParticleEmitterData\n" 73 "@see ParticleEmitterNode\n" 74); 75 76static const F32 sgDefaultWindCoefficient = 0.0f; 77static const F32 sgDefaultConstantAcceleration = 0.f; 78static const F32 sgDefaultSpinSpeed = 1.f; 79static const F32 sgDefaultSpinRandomMin = 0.f; 80static const F32 sgDefaultSpinRandomMax = 0.f; 81 82static const F32 sgDefaultSpinBias = 1.0f; 83static const F32 sgDefaultSizeBias = 1.0f; 84 85//----------------------------------------------------------------------------- 86// Constructor 87//----------------------------------------------------------------------------- 88ParticleData::ParticleData() 89{ 90 dragCoefficient = 0.0f; 91 windCoefficient = sgDefaultWindCoefficient; 92 gravityCoefficient = 0.0f; 93 inheritedVelFactor = 0.0f; 94 constantAcceleration = sgDefaultConstantAcceleration; 95 lifetimeMS = 1000; 96 lifetimeVarianceMS = 0; 97 spinSpeed = sgDefaultSpinSpeed; 98 spinRandomMin = sgDefaultSpinRandomMin; 99 spinRandomMax = sgDefaultSpinRandomMax; 100 useInvAlpha = false; 101 animateTexture = false; 102 103 numFrames = 1; 104 framesPerSec = numFrames; 105 106 S32 i; 107 for( i=0; i<PDC_NUM_KEYS; i++ ) 108 { 109 colors[i].set( 1.0, 1.0, 1.0, 1.0 ); 110 sizes[i] = 1.0; 111 } 112 113 times[0] = 0.0f; 114 times[1] = 1.0f; 115 for (i = 2; i < PDC_NUM_KEYS; i++) 116 times[i] = -1.0f; 117 118 texCoords[0].set(0.0,0.0); // texture coords at 4 corners 119 texCoords[1].set(0.0,1.0); // of particle quad 120 texCoords[2].set(1.0,1.0); // (defaults to entire particle) 121 texCoords[3].set(1.0,0.0); 122 animTexTiling.set(0,0); // tiling dimensions 123 animTexFramesString = NULL; // string of animation frame indices 124 animTexUVs = NULL; // array of tile vertex UVs 125 textureName = NULL; // texture filename 126 textureHandle = NULL; // loaded texture handle 127 textureExtName = NULL; 128 textureExtHandle = NULL; 129 constrain_pos = false; 130 start_angle = 0.0f; 131 angle_variance = 0.0f; 132 sizeBias = sgDefaultSizeBias; 133 spinBias = sgDefaultSpinBias; 134 randomizeSpinDir = false; 135} 136 137//----------------------------------------------------------------------------- 138// Destructor 139//----------------------------------------------------------------------------- 140 141 142FRangeValidator dragCoefFValidator(0.f, 5.f); 143FRangeValidator gravCoefFValidator(-10.f, 10.f); 144FRangeValidator spinRandFValidator(-1000.f, 1000.f); 145 146//----------------------------------------------------------------------------- 147// initPersistFields 148//----------------------------------------------------------------------------- 149void ParticleData::initPersistFields() 150{ 151 addFieldV( "dragCoefficient", TYPEID< F32 >(), Offset(dragCoefficient, ParticleData), &dragCoefFValidator, 152 "Particle physics drag amount." ); 153 addField( "windCoefficient", TYPEID< F32 >(), Offset(windCoefficient, ParticleData), 154 "Strength of wind on the particles." ); 155 addFieldV( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData), &gravCoefFValidator, 156 "Strength of gravity on the particles." ); 157 addFieldV( "inheritedVelFactor", TYPEID< F32 >(), Offset(inheritedVelFactor, ParticleData), &CommonValidators::NormalizedFloat, 158 "Amount of emitter velocity to add to particle initial velocity." ); 159 addField( "constantAcceleration", TYPEID< F32 >(), Offset(constantAcceleration, ParticleData), 160 "Constant acceleration to apply to this particle." ); 161 addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleData), 162 "Time in milliseconds before this particle is destroyed." ); 163 addField( "lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleData), 164 "Variance in lifetime of particle, from 0 - lifetimeMS." ); 165 addField( "spinSpeed", TYPEID< F32 >(), Offset(spinSpeed, ParticleData), 166 "Speed at which to spin the particle." ); 167 addFieldV( "spinRandomMin", TYPEID< F32 >(), Offset(spinRandomMin, ParticleData), &spinRandFValidator, 168 "Minimum allowed spin speed of this particle, between -1000 and spinRandomMax." ); 169 addFieldV( "spinRandomMax", TYPEID< F32 >(), Offset(spinRandomMax, ParticleData), &spinRandFValidator, 170 "Maximum allowed spin speed of this particle, between spinRandomMin and 1000." ); 171 addField( "useInvAlpha", TYPEID< bool >(), Offset(useInvAlpha, ParticleData), 172 "@brief Controls how particles blend with the scene.\n\n" 173 "If true, particles blend like ParticleBlendStyle NORMAL, if false, " 174 "blend like ParticleBlendStyle ADDITIVE.\n" 175 "@note If ParticleEmitterData::blendStyle is set, it will override this value." ); 176 addField( "animateTexture", TYPEID< bool >(), Offset(animateTexture, ParticleData), 177 "If true, allow the particle texture to be an animated sprite." ); 178 addField( "framesPerSec", TYPEID< S32 >(), Offset(framesPerSec, ParticleData), 179 "If animateTexture is true, this defines the frames per second of the " 180 "sprite animation." ); 181 182 addField( "textureCoords", TYPEID< Point2F >(), Offset(texCoords, ParticleData), 4, 183 "@brief 4 element array defining the UV coords into textureName to use " 184 "for this particle.\n\n" 185 "Coords should be set for the first tile only when using animTexTiling; " 186 "coordinates for other tiles will be calculated automatically. \"0 0\" is " 187 "top left and \"1 1\" is bottom right." ); 188 addField( "animTexTiling", TYPEID< Point2I >(), Offset(animTexTiling, ParticleData), 189 "@brief The number of frames, in rows and columns stored in textureName " 190 "(when animateTexture is true).\n\n" 191 "A maximum of 256 frames can be stored in a single texture when using " 192 "animTexTiling. Value should be \"NumColumns NumRows\", for example \"4 4\"." ); 193 addField( "animTexFrames", TYPEID< StringTableEntry >(), Offset(animTexFramesString,ParticleData), 194 "@brief A list of frames and/or frame ranges to use for particle " 195 "animation if animateTexture is true.\n\n" 196 "Each frame token must be separated by whitespace. A frame token must be " 197 "a positive integer frame number or a range of frame numbers separated " 198 "with a '-'. The range separator, '-', cannot have any whitspace around " 199 "it.\n\n" 200 "Ranges can be specified to move through the frames in reverse as well " 201 "as forward (eg. 19-14). Frame numbers exceeding the number of tiles will " 202 "wrap.\n" 203 "@tsexample\n" 204 "animTexFrames = \"0-16 20 19 18 17 31-21\";\n" 205 "@endtsexample\n" ); 206 207 addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData), 208 "Texture file to use for this particle." ); 209 addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData), 210 "@brief Texture file to use for this particle if animateTexture is true.\n\n" 211 "Deprecated. Use textureName instead." ); 212 213 // Interpolation variables 214 addField( "colors", TYPEID< LinearColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS, 215 "@brief Particle RGBA color keyframe values.\n\n" 216 "The particle color will linearly interpolate between the color/time keys " 217 "over the lifetime of the particle." ); 218 addProtectedField( "sizes", TYPEID< F32 >(), Offset(sizes, ParticleData), &protectedSetSizes, 219 &defaultProtectedGetFn, PDC_NUM_KEYS, 220 "@brief Particle size keyframe values.\n\n" 221 "The particle size will linearly interpolate between the size/time keys " 222 "over the lifetime of the particle." ); 223 addProtectedField( "times", TYPEID< F32 >(), Offset(times, ParticleData), &protectedSetTimes, 224 &defaultProtectedGetFn, PDC_NUM_KEYS, 225 "@brief Time keys used with the colors and sizes keyframes.\n\n" 226 "Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." ); 227 228 addGroup("AFX"); 229 addField("textureExtName", TypeFilename, Offset(textureExtName, ParticleData)); 230 addField("constrainPos", TypeBool, Offset(constrain_pos, ParticleData)); 231 addField("angle", TypeF32, Offset(start_angle, ParticleData)); 232 addField("angleVariance", TypeF32, Offset(angle_variance, ParticleData)); 233 addField("sizeBias", TypeF32, Offset(sizeBias, ParticleData)); 234 addField("spinBias", TypeF32, Offset(spinBias, ParticleData)); 235 addField("randomizeSpinDir", TypeBool, Offset(randomizeSpinDir, ParticleData)); 236 endGroup("AFX"); 237 Parent::initPersistFields(); 238} 239 240//----------------------------------------------------------------------------- 241// Pack data 242//----------------------------------------------------------------------------- 243void ParticleData::packData(BitStream* stream) 244{ 245 Parent::packData(stream); 246 247 stream->writeFloat(dragCoefficient / 5, 10); 248 if( stream->writeFlag(windCoefficient != sgDefaultWindCoefficient ) ) 249 stream->write(windCoefficient); 250 if (stream->writeFlag(gravityCoefficient != 0.0f)) 251 stream->writeSignedFloat(gravityCoefficient / 10, 12); 252 stream->writeFloat(inheritedVelFactor, 9); 253 if( stream->writeFlag( constantAcceleration != sgDefaultConstantAcceleration ) ) 254 stream->write(constantAcceleration); 255 256 stream->write( lifetimeMS ); 257 stream->write( lifetimeVarianceMS ); 258 259 if( stream->writeFlag( spinSpeed != sgDefaultSpinSpeed ) ) 260 stream->write(spinSpeed); 261 if(stream->writeFlag(spinRandomMin != sgDefaultSpinRandomMin || spinRandomMax != sgDefaultSpinRandomMax)) 262 { 263 stream->writeInt((S32)(spinRandomMin + 1000), 11); 264 stream->writeInt((S32)(spinRandomMax + 1000), 11); 265 } 266 if(stream->writeFlag(spinBias != sgDefaultSpinBias)) 267 stream->write(spinBias); 268 stream->writeFlag(randomizeSpinDir); 269 stream->writeFlag(useInvAlpha); 270 271 S32 i, count; 272 273 // see how many frames there are: 274 for(count = 0; count < ParticleData::PDC_NUM_KEYS-1; count++) 275 if(times[count] >= 1) 276 break; 277 278 count++; 279 280 // An extra bit is needed for 8 keys. 281 stream->writeInt(count-1, 3); 282 283 for( i=0; i<count; i++ ) 284 { 285 stream->writeFloat( colors[i].red, 7); 286 stream->writeFloat( colors[i].green, 7); 287 stream->writeFloat( colors[i].blue, 7); 288 stream->writeFloat( colors[i].alpha, 7); 289 // AFX bits raised from 14 to 16 to allow larger sizes 290 stream->writeFloat( sizes[i]/<a href="/coding/file/particle_8h/#particle_8h_1acba9461aeb4fafe9946d9468cb99f0c5">MaxParticleSize</a>, 16); 291 stream->writeFloat( times[i], 8); 292 } 293 294 if (stream->writeFlag(textureName && textureName[0])) 295 stream->writeString(textureName); 296 for (i = 0; i < 4; i++) 297 mathWrite(*stream, texCoords[i]); 298 if (stream->writeFlag(animateTexture)) 299 { 300 if (stream->writeFlag(animTexFramesString && animTexFramesString[0])) 301 { 302 stream->writeString(animTexFramesString); 303 } 304 mathWrite(*stream, animTexTiling); 305 stream->writeInt(framesPerSec, 8); 306 } 307 if (stream->writeFlag(textureExtName && textureExtName[0])) 308 stream->writeString(textureExtName); 309 stream->writeFlag(constrain_pos); 310 stream->writeFloat(start_angle/360.0f, 11); 311 stream->writeFloat(angle_variance/180.0f, 10); 312 if(stream->writeFlag(sizeBias != sgDefaultSizeBias)) 313 stream->write(sizeBias); 314} 315 316//----------------------------------------------------------------------------- 317// Unpack data 318//----------------------------------------------------------------------------- 319void ParticleData::unpackData(BitStream* stream) 320{ 321 Parent::unpackData(stream); 322 323 dragCoefficient = stream->readFloat(10) * 5; 324 if(stream->readFlag()) 325 stream->read(&windCoefficient); 326 else 327 windCoefficient = sgDefaultWindCoefficient; 328 if (stream->readFlag()) 329 gravityCoefficient = stream->readSignedFloat(12)*10; 330 else 331 gravityCoefficient = 0.0f; 332 inheritedVelFactor = stream->readFloat(9); 333 if(stream->readFlag()) 334 stream->read(&constantAcceleration); 335 else 336 constantAcceleration = sgDefaultConstantAcceleration; 337 338 stream->read( &lifetimeMS ); 339 stream->read( &lifetimeVarianceMS ); 340 341 if(stream->readFlag()) 342 stream->read(&spinSpeed); 343 else 344 spinSpeed = sgDefaultSpinSpeed; 345 346 if(stream->readFlag()) 347 { 348 spinRandomMin = (F32)(stream->readInt(11) - 1000); 349 spinRandomMax = (F32)(stream->readInt(11) - 1000); 350 } 351 else 352 { 353 spinRandomMin = sgDefaultSpinRandomMin; 354 spinRandomMax = sgDefaultSpinRandomMax; 355 } 356 357 if(stream->readFlag()) 358 stream->read(&spinBias); 359 else 360 spinBias = sgDefaultSpinBias; 361 randomizeSpinDir = stream->readFlag(); 362 useInvAlpha = stream->readFlag(); 363 364 S32 i; 365 // An extra bit is needed for 8 keys. 366 S32 count = stream->readInt(3) + 1; 367 for(i = 0;i < count; i++) 368 { 369 colors[i].red = stream->readFloat(7); 370 colors[i].green = stream->readFloat(7); 371 colors[i].blue = stream->readFloat(7); 372 colors[i].alpha = stream->readFloat(7); 373 // AFX bits raised from 14 to 16 to allow larger sizes 374 sizes[i] = stream->readFloat(16) * MaxParticleSize; 375 times[i] = stream->readFloat(8); 376 } 377 textureName = (stream->readFlag()) ? stream->readSTString() : 0; 378 for (i = 0; i < 4; i++) 379 mathRead(*stream, &texCoords[i]); 380 381 animateTexture = stream->readFlag(); 382 if (animateTexture) 383 { 384 animTexFramesString = (stream->readFlag()) ? stream->readSTString() : 0; 385 mathRead(*stream, &animTexTiling); 386 framesPerSec = stream->readInt(8); 387 } 388 textureExtName = (stream->readFlag()) ? stream->readSTString() : 0; 389 constrain_pos = stream->readFlag(); 390 start_angle = 360.0f*stream->readFloat(11); 391 angle_variance = 180.0f*stream->readFloat(10); 392 if(stream->readFlag()) 393 stream->read(&sizeBias); 394 else 395 sizeBias = sgDefaultSizeBias; 396} 397 398bool ParticleData::protectedSetSizes( void *object, const char *index, const char *data) 399{ 400 ParticleData *pData = static_cast<ParticleData*>( object ); 401 F32 val = dAtof(data); 402 U32 i; 403 404 if (!index) 405 return (val >= 0.f && val <= MaxParticleSize); 406 else 407 i = dAtoui(index); 408 409 pData->sizes[i] = mClampF( val, 0.f, MaxParticleSize ); 410 411 return false; 412} 413 414bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data) 415{ 416 ParticleData *pData = static_cast<ParticleData*>( object ); 417 F32 val = dAtof(data); 418 U32 i; 419 420 if (!index) 421 return (val >= 0.f && val <= 1.f); 422 else 423 i = dAtoui(index); 424 425 pData->times[i] = mClampF( val, 0.f, 1.f ); 426 427 return false; 428} 429 430//----------------------------------------------------------------------------- 431// onAdd 432//----------------------------------------------------------------------------- 433bool ParticleData::onAdd() 434{ 435 if (Parent::onAdd() == false) 436 return false; 437 438 if (dragCoefficient < 0.0) { 439 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) drag coeff less than 0", getName()); 440 dragCoefficient = 0.0f; 441 } 442 if (lifetimeMS < 1) { 443 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetime < 1 ms", getName()); 444 lifetimeMS = 1; 445 } 446 if (lifetimeVarianceMS >= lifetimeMS) { 447 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetimeVariance >= lifetime", getName()); 448 lifetimeVarianceMS = lifetimeMS - 1; 449 } 450 if (spinSpeed > 1000.f || spinSpeed < -1000.f) { 451 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinSpeed invalid", getName()); 452 return false; 453 } 454 if (spinRandomMin > 1000.f || spinRandomMin < -1000.f) { 455 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin invalid", getName()); 456 spinRandomMin = -360.0; 457 return false; 458 } 459 if (spinRandomMin > spinRandomMax) { 460 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin greater than spinRandomMax", getName()); 461 spinRandomMin = spinRandomMax - (spinRandomMin - spinRandomMax ); 462 return false; 463 } 464 if (spinRandomMax > 1000.f || spinRandomMax < -1000.f) { 465 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMax invalid", getName()); 466 spinRandomMax = 360.0; 467 return false; 468 } 469 if (framesPerSec > 255) 470 { 471 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) framesPerSec > 255, too high", getName()); 472 framesPerSec = 255; 473 return false; 474 } 475 476 times[0] = 0.0f; 477 for (U32 i = 1; i < PDC_NUM_KEYS; i++) 478 { 479 if (times[i] < 0.0f) 480 break; 481 if (times[i] < times[i-1]) 482 { 483 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1); 484 times[i] = times[i-1]; 485 } 486 } 487 488 times[0] = 0.0f; 489 490 U32 last_idx = 0; 491 for (U32 i = 1; i < PDC_NUM_KEYS; i++) 492 { 493 if (times[i] < 0.0f) 494 break; 495 else 496 last_idx = i; 497 } 498 499 for (U32 i = last_idx+1; i < PDC_NUM_KEYS; i++) 500 { 501 times[i] = times[last_idx]; 502 colors[i] = colors[last_idx]; 503 sizes[i] = sizes[last_idx]; 504 } 505 506 // Here we validate parameters 507 if (animateTexture) 508 { 509 // Tiling dimensions must be positive and non-zero 510 if (animTexTiling.x <= 0 || animTexTiling.y <= 0) 511 { 512 Con::warnf(ConsoleLogEntry::General, 513 "ParticleData(%s) bad value(s) for animTexTiling [%d or %d <= 0], invalid datablock", 514 animTexTiling.x, animTexTiling.y, getName()); 515 return false; 516 } 517 518 // Indices must fit into a byte so these are also bad 519 if (animTexTiling.x * animTexTiling.y > 256) 520 { 521 Con::warnf(ConsoleLogEntry::General, 522 "ParticleData(%s) bad values for animTexTiling [%d*%d > %d], invalid datablock", 523 animTexTiling.x, animTexTiling.y, 256, getName()); 524 return false; 525 } 526 527 // A list of frames is required 528 if (!animTexFramesString || !animTexFramesString[0]) 529 { 530 Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) no animTexFrames, invalid datablock", getName()); 531 return false; 532 } 533 534 // The frame list cannot be too long. 535 if (animTexFramesString && dStrlen(animTexFramesString) > 255) 536 { 537 Con::errorf(ConsoleLogEntry::General, "ParticleData(%s) animTexFrames string too long [> 255 chars]", getName()); 538 return false; 539 } 540 } 541 542 start_angle = mFmod(start_angle, 360.0f); 543 if (start_angle < 0.0f) 544 start_angle += 360.0f; 545 angle_variance = mClampF(angle_variance, -180.0f, 180.0f); 546 return true; 547} 548 549//----------------------------------------------------------------------------- 550// preload 551//----------------------------------------------------------------------------- 552bool ParticleData::preload(bool server, String &errorStr) 553{ 554 if (Parent::preload(server, errorStr) == false) 555 return false; 556 557 bool error = false; 558 if(!server) 559 { 560 // Here we attempt to load the particle's texture if specified. An undefined 561 // texture is *not* an error since the emitter may provide one. 562 if (textureName && textureName[0]) 563 { 564 textureHandle = GFXTexHandle(textureName, &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__)); 565 if (!textureHandle) 566 { 567 errorStr = String::ToString("Missing particle texture: %s", textureName); 568 error = true; 569 } 570 } 571 if (textureExtName && textureExtName[0]) 572 { 573 textureExtHandle = GFXTexHandle(textureExtName, &GFXStaticTextureSRGBProfile, avar("%s() - textureExtHandle (line %d)", __FUNCTION__, __LINE__)); 574 if (!textureExtHandle) 575 { 576 errorStr = String::ToString("Missing particle texture: %s", textureName); 577 error = true; 578 } 579 } 580 581 if (animateTexture) 582 { 583 // Here we parse animTexFramesString into byte-size frame numbers in animTexFrames. 584 // Each frame token must be separated by whitespace. 585 // A frame token must be a positive integer frame number or a range of frame numbers 586 // separated with a '-'. 587 // The range separator, '-', cannot have any whitspace around it. 588 // Ranges can be specified to move through the frames in reverse as well as forward. 589 // Frame numbers exceeding the number of tiles will wrap. 590 // example: 591 // "0-16 20 19 18 17 31-21" 592 593 S32 n_tiles = animTexTiling.x * animTexTiling.y; 594 AssertFatal(n_tiles > 0 && n_tiles <= 256, "Error, bad animTexTiling setting." ); 595 596 animTexFrames.clear(); 597 598 dsize_t tokLen = dStrlen(animTexFramesString) + 1; 599 char* tokCopy = new char[tokLen]; 600 dStrcpy(tokCopy, animTexFramesString, tokLen); 601 602 char* currTok = dStrtok(tokCopy, " \t"); 603 while (currTok != NULL) 604 { 605 char* minus = dStrchr(currTok, '-'); 606 if (minus) 607 { 608 // add a range of frames 609 *minus = '\0'; 610 S32 range_a = dAtoi(currTok); 611 S32 range_b = dAtoi(minus+1); 612 if (range_b < range_a) 613 { 614 // reverse frame range 615 for (S32 i = range_a; i >= range_b; i--) 616 animTexFrames.push_back((U8)(i % n_tiles)); 617 } 618 else 619 { 620 // forward frame range 621 for (S32 i = range_a; i <= range_b; i++) 622 animTexFrames.push_back((U8)(i % n_tiles)); 623 } 624 } 625 else 626 { 627 // add one frame 628 animTexFrames.push_back((U8)(dAtoi(currTok) % n_tiles)); 629 } 630 currTok = dStrtok(NULL, " \t"); 631 } 632 633 // Here we pre-calculate the UVs for each frame tile, which are 634 // tiled inside the UV region specified by texCoords. Since the 635 // UVs are calculated using bilinear interpolation, the texCoords 636 // region does *not* have to be an axis-aligned rectangle. 637 638 if (animTexUVs) 639 delete [] animTexUVs; 640 641 animTexUVs = new Point2F[(animTexTiling.x+1)*(animTexTiling.y+1)]; 642 643 // interpolate points on the left and right edge of the uv quadrangle 644 Point2F lf_pt = texCoords[0]; 645 Point2F rt_pt = texCoords[3]; 646 647 // per-row delta for left and right interpolated points 648 Point2F lf_d = (texCoords[1] - texCoords[0])/(F32)animTexTiling.y; 649 Point2F rt_d = (texCoords[2] - texCoords[3])/(F32)animTexTiling.y; 650 651 S32 idx = 0; 652 for (S32 yy = 0; yy <= animTexTiling.y; yy++) 653 { 654 Point2F p = lf_pt; 655 Point2F dp = (rt_pt - lf_pt)/(F32)animTexTiling.x; 656 for (S32 xx = 0; xx <= animTexTiling.x; xx++) 657 { 658 animTexUVs[idx++] = p; 659 p += dp; 660 } 661 lf_pt += lf_d; 662 rt_pt += rt_d; 663 } 664 665 // cleanup 666 delete [] tokCopy; 667 numFrames = animTexFrames.size(); 668 } 669 } 670 671 return !error; 672} 673 674//----------------------------------------------------------------------------- 675// Initialize particle 676//----------------------------------------------------------------------------- 677void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelocity) 678{ 679 init->dataBlock = this; 680 681 // Calculate the constant accleration... 682 init->vel += inheritVelocity * inheritedVelFactor; 683 init->acc = init->vel * constantAcceleration; 684 685 // Calculate this instance's lifetime... 686 init->totalLifetime = lifetimeMS; 687 if (lifetimeVarianceMS != 0) 688 init->totalLifetime += S32(gRandGen.randI() % (2 * lifetimeVarianceMS + 1)) - S32(lifetimeVarianceMS); 689 690 // assign spin amount 691 init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax ); 692 // apply spin bias 693 init->spinSpeed *= spinBias; 694 // randomize spin direction 695 if (randomizeSpinDir && (gRandGen.randI( 0, 1 ) == 1)) 696 init->spinSpeed = -init->spinSpeed; 697} 698 699bool ParticleData::reload(char errorBuffer[256]) 700{ 701 bool error = false; 702 if (textureName && textureName[0]) 703 { 704 textureHandle = GFXTexHandle(textureName, &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__)); 705 if (!textureHandle) 706 { 707 dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName); 708 error = true; 709 } 710 } 711 /* 712 numFrames = 0; 713 for( S32 i=0; i<PDC_MAX_TEX; i++ ) 714 { 715 if( textureNameList[i] && textureNameList[i][0] ) 716 { 717 textureList[i] = TextureHandle( textureNameList[i], MeshTexture ); 718 if (!textureList[i].getName()) 719 { 720 dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureNameList[i]); 721 error = true; 722 } 723 numFrames++; 724 } 725 } 726 */ 727 return !error; 728} 729 730DefineEngineMethod(ParticleData, reload, void, (),, 731 "Reloads this particle.\n" 732 "@tsexample\n" 733 "// Get the editor's current particle\n" 734 "%particle = PE_ParticleEditor.currParticle\n\n" 735 "// Change a particle value\n" 736 "%particle.setFieldValue( %propertyField, %value );\n\n" 737 "// Reload it\n" 738 "%particle.reload();\n" 739 "@endtsexample\n" ) 740{ 741 char errorBuffer[256]; 742 object->reload(errorBuffer); 743} 744//#define TRACK_PARTICLE_DATA_CLONES 745 746#ifdef TRACK_PARTICLE_DATA_CLONES 747static int particle_data_clones = 0; 748#endif 749 750ParticleData::ParticleData(const ParticleData& other, bool temp_clone) : SimDataBlock(other, temp_clone) 751{ 752#ifdef TRACK_PARTICLE_DATA_CLONES 753 particle_data_clones++; 754 if (particle_data_clones == 1) 755 Con::errorf("ParticleData -- Clones are on the loose!"); 756#endif 757 758 dragCoefficient = other.dragCoefficient; 759 windCoefficient = other.windCoefficient; 760 gravityCoefficient = other.gravityCoefficient; 761 inheritedVelFactor = other.inheritedVelFactor; 762 constantAcceleration = other.constantAcceleration; 763 lifetimeMS = other.lifetimeMS; 764 lifetimeVarianceMS = other.lifetimeVarianceMS; 765 spinSpeed = other.spinSpeed; 766 spinRandomMin = other.spinRandomMin; 767 spinRandomMax = other.spinRandomMax; 768 useInvAlpha = other.useInvAlpha; 769 animateTexture = other.animateTexture; 770 numFrames = other.numFrames; // -- calc from other fields 771 framesPerSec = other.framesPerSec; 772 dMemcpy( colors, other.colors, sizeof( colors ) ); 773 dMemcpy( sizes, other.sizes, sizeof( sizes ) ); 774 dMemcpy( times, other.times, sizeof( times ) ); 775 animTexUVs = other.animTexUVs; // -- calc from other fields 776 dMemcpy( texCoords, other.texCoords, sizeof( texCoords ) ); 777 animTexTiling = other.animTexTiling; 778 animTexFramesString = other.animTexFramesString; 779 animTexFrames = other.animTexFrames; // -- parsed from animTexFramesString 780 textureName = other.textureName; 781 textureHandle = other.textureHandle; 782 spinBias = other.spinBias; 783 randomizeSpinDir = other.randomizeSpinDir; 784 textureExtName = other.textureExtName; 785 textureExtHandle = other.textureExtHandle; 786 constrain_pos = other.constrain_pos; 787 start_angle = other.start_angle; 788 angle_variance = other.angle_variance; 789 sizeBias = other.sizeBias; 790} 791 792ParticleData::~ParticleData() 793{ 794 if (animTexUVs) 795 { 796 delete [] animTexUVs; 797 } 798 799 if (!isTempClone()) 800 return; 801 802#ifdef TRACK_PARTICLE_DATA_CLONES 803 if (particle_data_clones > 0) 804 { 805 particle_data_clones--; 806 if (particle_data_clones == 0) 807 Con::errorf("ParticleData -- Clones eliminated!"); 808 } 809 else 810 Con::errorf("ParticleData -- Too many clones deleted!"); 811#endif 812} 813 814void ParticleData::onPerformSubstitutions() 815{ 816 char errorBuffer[256]; 817 reload(errorBuffer); 818} 819