Intro

This article describes a custom particle affector which can be used to affect the size, rotation and velocity of particles in a particle system. The affector can be assigned to a particle system via a particle script, see the manual section 3.3 Particle Scripts for more details about that.

I found that the standard particle affectors don't quite give enough control over particles, they generally only allow you to modify particle properties in a linear fashion. This is why I have written the Interpolation Affector. It works similarly to the ColourInterpolator Affector but it is used to control size, rotation and velocity rather than colour. There are also a few differences in the way the Affector parameters are entered. Here is an example showing the use of the script:

Script

affector Interpolation
 {
     relative_velocity true
     values   0 1   0.5 2   1 0
 }

Using this will cause the velocity of each particle to double at half their life-time then go to zero at the end of their life. The values flag is a series of pairs of values where the first number represents the time and the second represents the relative velocity in this case (since the relative_velocity is set to true). The above line values 0 1 0.5 2 1 0 can be read as:

Time Value
0 1
0.5 2
1 0
The Interpolation affector can also be used to set the spin velocity and relative size by setting the spin_velocity and/or relative_size flags to true:
affector Interpolation
 {
     spin_velocity true
     values   0 10   1 -10
 }
 affector Interpolation
 {
     relative_size true
     values   0 0.1   0.2 1   1 1
 }

Note that several instances of the affector can be used in the same particle system to control size, spin and velocity independently. If on the other hand, you wish to change size, spin and or velocity with the same values then it is more efficient to use one instance of the affector with the relevant flags set. For example:

affector Interpolation
 {
     relative_velocity true
     relative_size true
     values   0 0.1   0.2 1   1 1
 }

This will vary velocity and size at the same time.

Limitations

Relative Velocity

If relative velocity is set to zero at some point during the particle's lifetime it is no longer possible to increase velocity again. The reason for this is that the particle stores speed and direction of a particle together in the form of a single vector. When the speed goes to 0, the velocity vector becomes zero and hence the direction the particle was travelling in is also lost. If you really want your particle to stop moving and start again then set the velocity to something very small so that the direction of the particle is kept. Something like 0.001 ought to do it, don't make it to low or numericals error may cause the velocity to be in-accurate (speed and direction) once it speeds up again.

Particle Script

The values parameter can be quite cryptic, this is due to a limitation in the way the particle script works. An affector gets input from a dictionary of parameters. The number of affector parameters must be specified at compile time. This leaves two options, one is to hard code a limited number of interpolation values (this is what is done with the ColourInterpolator). The second option is to provide all interpolation values in one long string, this is what has been done here. The advantage with this method is that you are not limited a hard coded number of interpolation values at the cost of a rather messy input string.

Manually created particles

If a particle is created manually, the affector doesn't get a chance to initialise itself. This can cause problems when relative velocity is used and the relative velocity at time = 0 is not 1. For example if you set the initial relative velocity to 2. Then the affector scales the velocity by 2 in the _initParticle function. But since this function is never called with manually created particles this will not happen. The knock on effect is that the particle will have half the velocity you expected for the rest of its lifetime. This problem only occurs with velocity however.

Applications

Personally I have used the affector to create explosion effects where the velocity of a particle is initally very large and then reduces to zero after a short period. The size of particle also starts out at 0 then grows to the desired size over a short period of time. Using this affector in conjunction with the standard particle affects you should be able to create particle effects similar to those created by Particle Illusion. I have found that Particle Illusion is a good tool for rapidly creating and viewing particle effects. I would have liked to be able to import effects directly from Particle Illusion, however they are stored as binary files. And I have found no option to export effects from it.

Code

Here is the code, place the files alongside the rest of your source code and compile it into the project. In order for the affector to be recognised by Ogre and used in particle scripts you will need to register it with ParticleSystemManager. First an instance of InterpolationAffectorFactory must be created then call the function ParticleSystemManager::addAffectorFactory() with a pointer to the instance created. Make sure the instance remains valid until Ogre cleans itself up or your program will likely crash. Here is an example of how to register the class:

InterpolationAffectorFactory* InterpFactory = new InterpolationAffectorFactory();
 ParticleSystemManager::getSingleton().addAffectorFactory(InterpFactory);

Insert this code to free memory after Ogre has cleaned up:

delete InterpFactory;

InterpolationAffectorFactory.h

#ifndef __InterpolationAffectorFactory_h__
 #define __InterpolationAffectorFactory_h__
 
 #include "OgreParticleFXPrerequisites.h"
 #include "OgreParticleAffectorFactory.h"
 #include "InterpolationAffector.h"
 
 using namespace Ogre;
 
 /** Factory class for InterpolationAffector. */
 class InterpolationAffectorFactory  : public ParticleAffectorFactory
 {
     /** See ParticleAffectorFactory */
     String getName() const { return "Interpolation"; }
 
     /** See ParticleAffectorFactory */
     ParticleAffector* createAffector(ParticleSystem* psys)
     {
         ParticleAffector* p = new InterpolationAffector(psys);
         mAffectors.push_back(p);
         return p;
     }
 };
 
 #endif

InterpolationAffector.h

#ifndef __InterpolationAffector_h__
 #define __InterpolationAffector_h__
 
 #include "OgreParticleFXPrerequisites.h"
 #include "OgreParticleAffector.h"
 #include "OgreVector3.h"
 
 using namespace Ogre;
 
 typedef std::vector<std::pair<Real, Real> > RealPairVector;
 
 class InterpolationAffector : public ParticleAffector
 {
 public:
     class CmdRelativeVelocity : public ParamCommand
     {
     public:
         String doGet(const void* target) const;
         void doSet(void* target, const String& val);
     };
 
     class CmdSpinVelocity : public ParamCommand
     {
     public:
         String doGet(const void* target) const;
         void doSet(void* target, const String& val);
     };
 
     class CmdRelativeSize : public ParamCommand
     {
     public:
         String doGet(const void* target) const;
         void doSet(void* target, const String& val);
     };
 
     class CmdInterpValues : public ParamCommand
     {
     public:
         String doGet(const void* target) const;
         void doSet(void* target, const String& val);
     };
 
     /// Default constructor
     InterpolationAffector(ParticleSystem* psys);
 
     /** See ParticleAffector. */
     void _affectParticles(ParticleSystem* pSystem, Real timeElapsed);
     /** See ParticleAffector. */
     void _initParticle(Particle*  pParticle);
 
 
     void setRelativeVelocity(bool relativeVelocity);
     void setSpinVelocity(bool spinVelocity);
     void setRelativeSize(bool relativeSize);
     void setInterpValues(RealPairVector &interpValues);
 
     bool getRelativeVelocity(void) const;
     bool getSpinVelocity(void) const;
     bool getRelativeSize(void) const;
     const RealPairVector &getInterpValues() const;
 
 
     /// Command objects
     static CmdRelativeVelocity msRelativeVelocityCmd;
     static CmdSpinVelocity msSpinVelocityCmd;
     static CmdRelativeSize msRelativeSizeCmd;
     static CmdInterpValues msInterpValuesCmd;
 
 protected:
     Real getValue(Real time);
 
     RealPairVector mInterpValues;
     bool mRelativeVelocity;
     bool mSpinVelocity;
     bool mRelativeSize;
 };
 
 #endif

InterpolationAffector.cpp

#include "InterpolationAffector.h"
 #include "OgreParticleSystem.h"
 #include "OgreParticle.h"
 #include "OgreStringConverter.h"
 
 using namespace Ogre; 
 
 // Instantiate statics
 InterpolationAffector::CmdRelativeVelocity InterpolationAffector::msRelativeVelocityCmd;
 InterpolationAffector::CmdSpinVelocity InterpolationAffector::msSpinVelocityCmd;
 InterpolationAffector::CmdRelativeSize InterpolationAffector::msRelativeSizeCmd;
 InterpolationAffector::CmdInterpValues InterpolationAffector::msInterpValuesCmd;
 
 //-----------------------------------------------------------------------
 InterpolationAffector::InterpolationAffector(ParticleSystem* psys)
     : ParticleAffector(psys)
 {
     mType = "Interpolation";
 
     // defaults
     mRelativeVelocity = false;
     mSpinVelocity = false;
     mRelativeSize = false;
 
     // Set up parameters
     if (createParamDictionary("Interpolation"))
     {
         addBaseParameters();
         // Add extra paramaters
         ParamDictionary* dict = getParamDictionary();
         dict->addParameter(ParameterDef("relative_velocity",
             "Detemines whether the velocity of the particles is scaled.",
             PT_BOOL), &msRelativeVelocityCmd);
         dict->addParameter(ParameterDef("spin_velocity",
             "Detemines whether the spin velocity of the particles is scaled.",
             PT_BOOL), &msSpinVelocityCmd);
         dict->addParameter(ParameterDef("relative_size",
             "Detemines whether the size of the particles is scaled.",
             PT_BOOL), &msRelativeSizeCmd);
         dict->addParameter(ParameterDef("values",
             "Values to interpolate between.",
             PT_REAL), &msInterpValuesCmd);
     }
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::_initParticle(Particle* pParticle)
 {
     if (mSpinVelocity)
         pParticle->rotationSpeed = getValue(0);
     if (mRelativeVelocity)
         pParticle->direction *= getValue(0);
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::_affectParticles(ParticleSystem* pSystem, Real  timeElapsed)
 {
     ParticleIterator pi = pSystem->_getIterator();
     Particle *p;
     Real prevLifeFrac, lifeFrac;
     Real prevValue, value;
     Real scale;
     Real Width = pSystem->getDefaultWidth(), Height = pSystem->getDefaultHeight();
 
     while (!pi.end())
     {
         p = pi.getNext();
         lifeFrac = 1-(p->timeToLive / p->totalTimeToLive);
         value = getValue(lifeFrac);
         if (mSpinVelocity)
         {
             p->rotationSpeed = Degree(value);
             p->setRotation(p->rotation+p->rotationSpeed*timeElapsed);
         }
         if (mRelativeSize)
         {
             p->setDimensions(Width*value, Height*value);
         }
 
         if (mRelativeVelocity)
         {
             prevLifeFrac = 1-((p->timeToLive+timeElapsed) / p->totalTimeToLive);
             prevValue = getValue(prevLifeFrac);
             // if prevValue is zero then this particle cannot be affected (division by 0)
             if (prevValue != 0)
             {
                 scale = value/prevValue;
                 p->direction *= scale;
             }
         }
     }
 }
 //-----------------------------------------------------------------------
 Real InterpolationAffector::getValue(Real time)
 {
     if (mInterpValues.empty())
         return 1;
     if (time <= mInterpValues.front().first)
         return mInterpValues.front().second;
     if (time >= mInterpValues.back().first)
         return mInterpValues.back().second;
     RealPairVector::iterator itCurrentValue;
     RealPairVector::iterator itPrevValue = mInterpValues.end();
     for (itCurrentValue = mInterpValues.begin(); itCurrentValue != mInterpValues.end(); ++itCurrentValue)
     {
         if (itPrevValue != mInterpValues.end())
         {
             if (time <= itCurrentValue->first)
             {
                 Real u = (time-itPrevValue->first)/(itCurrentValue->first-itPrevValue->first);
                 Real value = itPrevValue->second + u*(itCurrentValue->second-itPrevValue->second);
                 return value;
             }
         }
         itPrevValue = itCurrentValue;
     }
     return 1;   // Should never get here
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::setRelativeVelocity(bool relativeVelocity)
 {
     mRelativeVelocity = relativeVelocity;
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::setSpinVelocity(bool spinVelocity)
 {
     mSpinVelocity = spinVelocity;
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::setRelativeSize(bool relativeSize)
 {
     mRelativeSize = relativeSize;
 }
 //-----------------------------------------------------------------------
 void InterpolationAffector::setInterpValues(RealPairVector &interpValues)
 {
     mInterpValues = interpValues;
 }
 //-----------------------------------------------------------------------
 bool InterpolationAffector::getRelativeVelocity(void) const
 {
     return mRelativeVelocity;
 }
 //-----------------------------------------------------------------------
 bool InterpolationAffector::getSpinVelocity(void) const
 {
     return mSpinVelocity;
 }
 //-----------------------------------------------------------------------
 bool InterpolationAffector::getRelativeSize(void) const
 {
     return mRelativeSize;
 }
 //-----------------------------------------------------------------------
 const RealPairVector &InterpolationAffector::getInterpValues() const
 {
     return mInterpValues;
 }
 
 
 //-----------------------------------------------------------------------
 //-----------------------------------------------------------------------
 // Command objects
 //-----------------------------------------------------------------------
 //-----------------------------------------------------------------------
 String InterpolationAffector::CmdRelativeVelocity::doGet(const void* target) const
 {
     return StringConverter::toString(
         static_cast<const InterpolationAffector*>(target)->getRelativeVelocity());
 }
 void InterpolationAffector::CmdRelativeVelocity::doSet(void* target, const String& val)
 {
     static_cast<InterpolationAffector*>(target)->setRelativeVelocity(
         StringConverter::parseBool(val));
 }
 String InterpolationAffector::CmdSpinVelocity::doGet(const void* target) const
 {
     return StringConverter::toString(
         static_cast<const InterpolationAffector*>(target)->getSpinVelocity());
 }
 void InterpolationAffector::CmdSpinVelocity::doSet(void* target, const String& val)
 {
     static_cast<InterpolationAffector*>(target)->setSpinVelocity(
         StringConverter::parseBool(val));
 }
 String InterpolationAffector::CmdRelativeSize::doGet(const void* target) const
 {
     return StringConverter::toString(
         static_cast<const InterpolationAffector*>(target)->getRelativeSize());
 }
 void InterpolationAffector::CmdRelativeSize::doSet(void* target, const String& val)
 {
     static_cast<InterpolationAffector*>(target)->setRelativeSize(
         StringConverter::parseBool(val));
 }
 String InterpolationAffector::CmdInterpValues::doGet(const  void* target) const
 {
     const RealPairVector &InterpValues =
         static_cast<const InterpolationAffector*>(target)->getInterpValues();
     String params;
     size_t numParams = InterpValues.size();
     unsigned int i;
     for (i=0; i<numParams; ++i)
     {
         params += StringConverter::toString(InterpValues[i].first);
         params += " ";
         params += StringConverter::toString(InterpValues[i].second);
         params += " ";
     }
     return params;
 }
 void InterpolationAffector::CmdInterpValues::doSet(void* target, const  String& val)
 {
     StringVector vecParams = StringUtil::split(val, " \t");
     size_t numParams = vecParams.size();
     unsigned int i;
     Real time;
     Real value;
     RealPairVector InterpValues;
     for (i=0; i<numParams-1; )
     {
         time = StringConverter::parseReal(vecParams[i++]);
         value = StringConverter::parseReal(vecParams[i++]);
         InterpValues.push_back(std::make_pair(time, value));
     }
     static_cast<InterpolationAffector*>(target)->setInterpValues(InterpValues);
 }

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.