diff --git a/Horde3D/Bindings/Python/horde3d/__init__.py b/Horde3D/Bindings/Python/horde3d/__init__.py index 398c75a..be56784 100644 --- a/Horde3D/Bindings/Python/horde3d/__init__.py +++ b/Horde3D/Bindings/Python/horde3d/__init__.py @@ -911,4 +911,5 @@ hasEmitterFinished.argtypes = [c_int] __all__.append('hasEmitterFinished') - +setUpdateFunction = h3d.h3dSetUpdateFunction +__all__.append('setUpdateFunction') diff --git a/Horde3D/Source/Horde3DEngine/egParticle.cpp b/Horde3D/Source/Horde3DEngine/egParticle.cpp index eb360e7..18cf52a 100644 --- a/Horde3D/Source/Horde3DEngine/egParticle.cpp +++ b/Horde3D/Source/Horde3DEngine/egParticle.cpp @@ -319,6 +319,8 @@ EmitterNode::EmitterNode( const EmitterNodeTpl &emitterTpl ) : _parSizesANDRotations = 0x0; _parColors = 0x0; + _updateFunction = 0x0; + setMaxParticleCount( _particleCount ); } @@ -526,6 +528,25 @@ void EmitterNode::setParamF( int param, int compIdx, float value ) } +DLLEXP void h3dSetUpdateFunction(NodeHandle node, EmitterNode::UpdateFunction f) +{ + SceneNode *sn = Modules::sceneMan().resolveNodeHandle(node); + if(!sn) + { + return; + } + + EmitterNode *en = (EmitterNode*)sn; + en->setUpdateFunction(f); +} + + +void EmitterNode::setUpdateFunction(UpdateFunction f) +{ + _updateFunction = f; +} + + float randomF( float min, float max ) { return (rand() / (float)RAND_MAX) * (max - min) + min; @@ -568,104 +589,114 @@ void EmitterNode::onPostUpdate() else _delay -= _timeDelta; - Vec3f motionVec = _absTrans.getTrans() - _prevAbsTrans.getTrans(); - - // Check how many particles will be spawned - float spawnCount = 0; - for( uint32 i = 0; i < _particleCount; ++i ) + if(_updateFunction) { - ParticleData &p = _particles[i]; - if( p.life <= 0 && ((int)p.respawnCounter < _respawnCount || _respawnCount < 0) ) - { - spawnCount += 1.0f; - if( spawnCount >= _emissionAccum ) break; - } + _updateFunction(_timeDelta, _particleCount, _particles, _parPositions, _parSizesANDRotations, _parColors); } - - // Particles are distributed along emitter's motion vector to avoid blobs when fps is low - float curStep = 0, stepWidth = 0.5f; - if( spawnCount > 2.0f ) stepWidth = motionVec.length() / spawnCount; - - for( uint32 i = 0; i < _particleCount; ++i ) + else { - ParticleData &p = _particles[i]; - - // Create particle - if( p.life <= 0 && ((int)p.respawnCounter < _respawnCount || _respawnCount < 0) ) + Vec3f motionVec = _absTrans.getTrans() - _prevAbsTrans.getTrans(); + + // Check how many particles will be spawned + float spawnCount = 0; + for( uint32 i = 0; i < _particleCount; ++i ) { - if( _emissionAccum >= 1.0f ) + ParticleData &p = _particles[i]; + if( p.life <= 0 && ((int)p.respawnCounter < _respawnCount || _respawnCount < 0) ) { - // Respawn - p.maxLife = randomF( _effectRes->_lifeMin, _effectRes->_lifeMax ); - p.life = p.maxLife; - float angle = degToRad( _spreadAngle / 2 ); - Matrix4f m = _absTrans; - m.c[3][0] = 0; m.c[3][1] = 0; m.c[3][2] = 0; - m.rotate( randomF( -angle, angle ), randomF( -angle, angle ), randomF( -angle, angle ) ); - p.dir = (m * Vec3f( 0, 0, -1 )).normalized(); - p.dragVec = motionVec / _timeDelta; - ++p.respawnCounter; - - // Generate start values - p.moveVel0 = randomF( _effectRes->_moveVel.startMin, _effectRes->_moveVel.startMax ); - p.rotVel0 = randomF( _effectRes->_rotVel.startMin, _effectRes->_rotVel.startMax ); - p.drag0 = randomF( _effectRes->_drag.startMin, _effectRes->_drag.startMax ); - p.size0 = randomF( _effectRes->_size.startMin, _effectRes->_size.startMax ); - p.r0 = randomF( _effectRes->_colR.startMin, _effectRes->_colR.startMax ); - p.g0 = randomF( _effectRes->_colG.startMin, _effectRes->_colG.startMax ); - p.b0 = randomF( _effectRes->_colB.startMin, _effectRes->_colB.startMax ); - p.a0 = randomF( _effectRes->_colA.startMin, _effectRes->_colA.startMax ); - - // Update arrays - _parPositions[i * 3 + 0] = _absTrans.c[3][0] - motionVec.x * curStep; - _parPositions[i * 3 + 1] = _absTrans.c[3][1] - motionVec.y * curStep; - _parPositions[i * 3 + 2] = _absTrans.c[3][2] - motionVec.z * curStep; - _parSizesANDRotations[i * 2 + 0] = p.size0; - _parSizesANDRotations[i * 2 + 1] = randomF( 0, 360 ); - _parColors[i * 4 + 0] = p.r0; - _parColors[i * 4 + 1] = p.g0; - _parColors[i * 4 + 2] = p.b0; - _parColors[i * 4 + 3] = p.a0; - - // Update emitter - _emissionAccum -= 1.f; - if( _emissionAccum < 0 ) _emissionAccum = 0.f; - - curStep += stepWidth; + spawnCount += 1.0f; + if( spawnCount >= _emissionAccum ) break; } } + + // Particles are distributed along emitter's motion vector to avoid blobs when fps is low + float curStep = 0, stepWidth = 0.5f; + if( spawnCount > 2.0f ) stepWidth = motionVec.length() / spawnCount; - // Update particle - if( p.life > 0 ) + for( uint32 i = 0; i < _particleCount; ++i ) { - // Interpolate data - float fac = 1.0f - (p.life / p.maxLife); + ParticleData &p = _particles[i]; - float moveVel = p.moveVel0 * (1.0f + (_effectRes->_moveVel.endRate - 1.0f) * fac); - float rotVel = p.rotVel0 * (1.0f + (_effectRes->_rotVel.endRate - 1.0f) * fac); - float drag = p.drag0 * (1.0f + (_effectRes->_drag.endRate - 1.0f) * fac); - _parSizesANDRotations[i * 2 + 0] = p.size0 * (1.0f + (_effectRes->_size.endRate - 1.0f) * fac); - _parColors[i * 4 + 0] = p.r0 * (1.0f + (_effectRes->_colR.endRate - 1.0f) * fac); - _parColors[i * 4 + 1] = p.g0 * (1.0f + (_effectRes->_colG.endRate - 1.0f) * fac); - _parColors[i * 4 + 2] = p.b0 * (1.0f + (_effectRes->_colB.endRate - 1.0f) * fac); - _parColors[i * 4 + 3] = p.a0 * (1.0f + (_effectRes->_colA.endRate - 1.0f) * fac); - - // Update particle position and rotation - _parPositions[i * 3 + 0] += (p.dir.x * moveVel + p.dragVec.x * drag + _force.x) * _timeDelta; - _parPositions[i * 3 + 1] += (p.dir.y * moveVel + p.dragVec.y * drag + _force.y) * _timeDelta; - _parPositions[i * 3 + 2] += (p.dir.z * moveVel + p.dragVec.z * drag + _force.z) * _timeDelta; - _parSizesANDRotations[i * 2 + 1] += rotVel * _timeDelta; - - // Decrease lifetime - p.life -= _timeDelta; + // Create particle + if( p.life <= 0 && ((int)p.respawnCounter < _respawnCount || _respawnCount < 0) ) + { + if( _emissionAccum >= 1.0f ) + { + // Respawn + p.maxLife = randomF( _effectRes->_lifeMin, _effectRes->_lifeMax ); + p.life = p.maxLife; + float angle = degToRad( _spreadAngle / 2 ); + Matrix4f m = _absTrans; + m.c[3][0] = 0; m.c[3][1] = 0; m.c[3][2] = 0; + m.rotate( randomF( -angle, angle ), randomF( -angle, angle ), randomF( -angle, angle ) ); + p.dir = (m * Vec3f( 0, 0, -1 )).normalized(); + p.dragVec = motionVec / _timeDelta; + ++p.respawnCounter; + + // Generate start values + p.moveVel0 = randomF( _effectRes->_moveVel.startMin, _effectRes->_moveVel.startMax ); + p.rotVel0 = randomF( _effectRes->_rotVel.startMin, _effectRes->_rotVel.startMax ); + p.drag0 = randomF( _effectRes->_drag.startMin, _effectRes->_drag.startMax ); + p.size0 = randomF( _effectRes->_size.startMin, _effectRes->_size.startMax ); + p.r0 = randomF( _effectRes->_colR.startMin, _effectRes->_colR.startMax ); + p.g0 = randomF( _effectRes->_colG.startMin, _effectRes->_colG.startMax ); + p.b0 = randomF( _effectRes->_colB.startMin, _effectRes->_colB.startMax ); + p.a0 = randomF( _effectRes->_colA.startMin, _effectRes->_colA.startMax ); + + // Update arrays + _parPositions[i * 3 + 0] = _absTrans.c[3][0] - motionVec.x * curStep; + _parPositions[i * 3 + 1] = _absTrans.c[3][1] - motionVec.y * curStep; + _parPositions[i * 3 + 2] = _absTrans.c[3][2] - motionVec.z * curStep; + _parSizesANDRotations[i * 2 + 0] = p.size0; + _parSizesANDRotations[i * 2 + 1] = randomF( 0, 360 ); + _parColors[i * 4 + 0] = p.r0; + _parColors[i * 4 + 1] = p.g0; + _parColors[i * 4 + 2] = p.b0; + _parColors[i * 4 + 3] = p.a0; + + // Update emitter + _emissionAccum -= 1.f; + if( _emissionAccum < 0 ) _emissionAccum = 0.f; + + curStep += stepWidth; + } + } - // Check if particle is dying - if( p.life <= 0 ) + // Update particle + if( p.life > 0 ) { - _parSizesANDRotations[i * 2 + 0] = 0.0f; + // Interpolate data + float fac = 1.0f - (p.life / p.maxLife); + + float moveVel = p.moveVel0 * (1.0f + (_effectRes->_moveVel.endRate - 1.0f) * fac); + float rotVel = p.rotVel0 * (1.0f + (_effectRes->_rotVel.endRate - 1.0f) * fac); + float drag = p.drag0 * (1.0f + (_effectRes->_drag.endRate - 1.0f) * fac); + _parSizesANDRotations[i * 2 + 0] = p.size0 * (1.0f + (_effectRes->_size.endRate - 1.0f) * fac); + _parColors[i * 4 + 0] = p.r0 * (1.0f + (_effectRes->_colR.endRate - 1.0f) * fac); + _parColors[i * 4 + 1] = p.g0 * (1.0f + (_effectRes->_colG.endRate - 1.0f) * fac); + _parColors[i * 4 + 2] = p.b0 * (1.0f + (_effectRes->_colB.endRate - 1.0f) * fac); + _parColors[i * 4 + 3] = p.a0 * (1.0f + (_effectRes->_colA.endRate - 1.0f) * fac); + + // Update particle position and rotation + _parPositions[i * 3 + 0] += (p.dir.x * moveVel + p.dragVec.x * drag + _force.x) * _timeDelta; + _parPositions[i * 3 + 1] += (p.dir.y * moveVel + p.dragVec.y * drag + _force.y) * _timeDelta; + _parPositions[i * 3 + 2] += (p.dir.z * moveVel + p.dragVec.z * drag + _force.z) * _timeDelta; + _parSizesANDRotations[i * 2 + 1] += rotVel * _timeDelta; + + // Decrease lifetime + p.life -= _timeDelta; + + // Check if particle is dying + if( p.life <= 0 ) + { + _parSizesANDRotations[i * 2 + 0] = 0.0f; + } } } + } + for( uint32 i = 0; i < _particleCount; ++i ) + { // Update bounding box Vec3f vertPos( _parPositions[i*3+0], _parPositions[i*3+1], _parPositions[i*3+2] ); if( vertPos.x < bBMin.x ) bBMin.x = vertPos.x; diff --git a/Horde3D/Source/Horde3DEngine/egParticle.h b/Horde3D/Source/Horde3DEngine/egParticle.h index b4e4323..98dabec 100644 --- a/Horde3D/Source/Horde3DEngine/egParticle.h +++ b/Horde3D/Source/Horde3DEngine/egParticle.h @@ -151,6 +151,8 @@ struct ParticleData class EmitterNode : public SceneNode { +public: + typedef void (*UpdateFunction)(float, uint32, void*, float*, float*, float*); // _timeDelta, _particleCount, _particles, _parPositions, _parSizesANDRotations, _parColors protected: // Emitter data @@ -172,6 +174,9 @@ protected: float *_parSizesANDRotations; float *_parColors; + // custom update function + UpdateFunction _updateFunction; + std::vector< uint32 > _occQueries; std::vector< uint32 > _lastVisited; @@ -191,6 +196,7 @@ public: void setParamI( int param, int value ); float getParamF( int param, int compIdx ); void setParamF( int param, int compIdx, float value ); + void setUpdateFunction(UpdateFunction f); void advanceTime( float timeDelta ); bool hasFinished();