Browse Source

Initial Undo/Redo support added -- redo doesn't work for the Line Edits ?

hdunderscore 11 years ago
parent
commit
ed78af0f1f

+ 28 - 0
Bin/Data/Particle/Disco.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<particleeffect>
+	<material name="Materials/Particle.xml" />
+	<numparticles value="10" />
+	<updateinvisible enable="true" />
+	<relative enable="true" />
+	<scaled enable="true" />
+	<sorted enable="true" />
+	<animlodbias value="0" />
+	<emittertype value="Sphere" />
+	<emittersize value="0 0 0" />
+	<direction min="-1 0 -1" max="1 1 1" />
+	<constantforce value="0 -2 0" />
+	<dampingforce value="0" />
+	<activetime value="0" />
+	<inactivetime value="0" />
+	<emissionrate min="50" max="50" />
+	<particlesize min="0.25 0.25" max="0.25 0.25" />
+	<timetolive min="2.01" max="2.41" />
+	<velocity min="2" max="3" />
+	<rotation min="0" max="0" />
+	<rotationspeed min="0" max="0" />
+	<sizedelta add="0" mul="1" />
+	<colorfade color="0.35 0.35 0.5 1" time="0" />
+	<colorfade color="0 0 0 1" time="0.5" />
+	<colorfade color="1 0 0 1" time="1" />
+	<colorfade color="0 1 0 1" time="2" />
+</particleeffect>

+ 27 - 0
Bin/Data/Particle/PoisonStack.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<particleeffect>
+	<material name="Materials/Smoke.xml" />
+	<numparticles value="1000" />
+	<updateinvisible enable="true" />
+	<relative enable="false" />
+	<scaled enable="true" />
+	<sorted enable="true" />
+	<animlodbias value="0" />
+	<emittertype value="Box" />
+	<emittersize value="1 1 1" />
+	<direction min="-0.15 1 -0.15" max="0.15 1 0.15" />
+	<constantforce value="0 2 0" />
+	<dampingforce value="2" />
+	<activetime value="0" />
+	<inactivetime value="0" />
+	<emissionrate min="100" max="200" />
+	<particlesize min="0.1 0.2" max="0.6 0.7" />
+	<timetolive min="4" max="4" />
+	<velocity min="0.5" max="3" />
+	<rotation min="0" max="0" />
+	<rotationspeed min="60" max="60" />
+	<sizedelta add="0" mul="1.3" />
+	<colorfade color="1 1 1 0" time="0" />
+	<colorfade color="0 0.33 0.2 0.5" time="0.64" />
+	<colorfade color="0 0 0 0" time="4" />
+</particleeffect>

+ 28 - 0
Bin/Data/Particle/SmokeStack.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<particleeffect>
+	<material name="Materials/Smoke.xml" />
+	<numparticles value="1000" />
+	<updateinvisible enable="true" />
+	<relative enable="false" />
+	<scaled enable="true" />
+	<sorted enable="true" />
+	<animlodbias value="0" />
+	<emittertype value="Box" />
+	<emittersize value="1 1 1" />
+	<direction min="-0.15 1 -0.15" max="0.15 1 0.15" />
+	<constantforce value="0 2 0" />
+	<dampingforce value="2" />
+	<activetime value="0" />
+	<inactivetime value="0" />
+	<emissionrate min="100" max="200" />
+	<particlesize min="0.1 0.2" max="0.6 0.7" />
+	<timetolive min="4" max="4" />
+	<velocity min="0.5" max="3" />
+	<rotation min="0" max="0" />
+	<rotationspeed min="60" max="60" />
+	<sizedelta add="0" mul="1.3" />
+	<colorfade color="1 1 1 0" time="0" />
+	<colorfade color="0.69 0.33 0.2 0.5" time="0.64" />
+	<colorfade color="0 0 0 0" time="1.98" />
+	<colorfade color="0 0 0 0" time="4" />
+</particleeffect>

+ 11 - 1
Bin/Data/Scripts/Editor.as

@@ -116,7 +116,17 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     {
         if (!particleEffectEmitter.emitting)
         {
-            particleEffectEmitter.Reset();
+            if (particleResetTimer == 0.0f)
+                particleResetTimer = editParticleEffect.maxTimeToLive + 0.2f;
+            else
+            {
+                particleResetTimer = Max(particleResetTimer - timeStep, 0.0f);
+                if (particleResetTimer <= 0.0001f)
+                {
+                    particleEffectEmitter.Reset();
+                    particleResetTimer = 0.0f;
+                }
+            }
         }
     }
 }

+ 24 - 26
Bin/Data/Scripts/Editor/EditorActions.as

@@ -792,44 +792,42 @@ class EditMaterialAction : EditAction
 
 class EditParticleEffectAction : EditAction
 {
+    XMLFile@ oldState;
+    XMLFile@ newState;
     WeakHandle particleEffect;
-    File@ oldEffect;
-    File@ newEffect;
+    ParticleEmitter@ particleEmitter;
 
-    void Define(ParticleEffect@ particleEffect_, XMLFile@ oldEffect_)
+    void Define(ParticleEmitter@ particleEmitter_, ParticleEffect@ particleEffect_, XMLFile@ oldState_)
     {
-        /*particleEffect = particleEffect_;
-        oldEffect = oldEffect_;
-        newEffect = File();
+        particleEmitter = particleEmitter_;
+        particleEffect = particleEffect_;
+        oldState = oldState_;
+        newState = XMLFile();
 
-        XMLElement effectElem = newEffect.CreateRoot("particleemitter");
-        particleEffect.Save(newEffect);*/
+        XMLElement particleElem = newState.CreateRoot("particleeffect");
+        particleEffect_.Save(particleElem);
     }
 
     void Undo()
     {
-        /*ParticleEmitter@ particleEmitter_ = particleEmitter.Get();
-        if (particleEmitter_ is null)
-            return;
-
-        ParticleEffect@ effect = cache.GetResource("ParticleEffect", oldEffect);
-        if (effect is null)
-            return;
-        particleEmitter_.effect = effect;*/
-        RefreshParticleEffectEditor();
+        ParticleEffect@ effect = particleEffect.Get();
+        if (effect !is null)
+        {
+            effect.Load(oldState.root);
+            particleEmitter.ApplyEffect();
+            RefreshParticleEffectEditor();
+        }
     }
 
     void Redo()
     {
-        /*ParticleEmitter@ particleEmitter_ = particleEmitter.Get();
-        if (particleEmitter_ is null)
-            return;
-
-        ParticleEffect@ effect = cache.GetResource("ParticleEffect", newEffect);
-        if (effect is null)
-            return;
-        particleEmitter_.effect = effect;*/
-        RefreshParticleEffectEditor();
+        ParticleEffect@ effect = particleEffect.Get();
+        if (effect !is null)
+        {
+            effect.Load(newState.root);
+            particleEmitter.ApplyEffect();
+            RefreshParticleEffectEditor();
+        }
     }
 }
 

+ 167 - 7
Bin/Data/Scripts/Editor/EditorParticleEffect.as

@@ -11,6 +11,7 @@ Node@ particlePreviewCameraNode;
 Node@ particlePreviewLightNode;
 Light@ particlePreviewLight;
 ParticleEmitter@ particleEffectEmitter;
+float particleResetTimer;
 
 void CreateParticleEffectEditor()
 {
@@ -75,6 +76,41 @@ void CreateParticleEffectEditor()
     SubscribeToEvent(particleEffectWindow.GetChild("EmitterSizeZ", true), "TextChanged", "EditParticleEffectEmitterSize");
     SubscribeToEvent(particleEffectWindow.GetChild("EmissionRateMin", true), "TextChanged", "EditParticleEffectEmissionRate");
     SubscribeToEvent(particleEffectWindow.GetChild("EmissionRateMax", true), "TextChanged", "EditParticleEffectEmissionRate");
+
+    SubscribeToEvent(particleEffectWindow.GetChild("ConstantForceX", true), "TextFinished", "EditParticleEffectConstantForce");
+    SubscribeToEvent(particleEffectWindow.GetChild("ConstantForceY", true), "TextFinished", "EditParticleEffectConstantForce");
+    SubscribeToEvent(particleEffectWindow.GetChild("ConstantForceZ", true), "TextFinished", "EditParticleEffectConstantForce");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMinX", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMinY", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMinZ", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMaxX", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMaxY", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DirectionMaxZ", true), "TextFinished", "EditParticleEffectDirection");
+    SubscribeToEvent(particleEffectWindow.GetChild("DampingForce", true), "TextFinished", "EditParticleEffectDampingForce");
+    SubscribeToEvent(particleEffectWindow.GetChild("ActiveTime", true), "TextFinished", "EditParticleEffectActiveTime");
+    SubscribeToEvent(particleEffectWindow.GetChild("InactiveTime", true), "TextFinished", "EditParticleEffectInactiveTime");
+    SubscribeToEvent(particleEffectWindow.GetChild("ParticleSizeMinX", true), "TextFinished", "EditParticleEffectParticleSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("ParticleSizeMinY", true), "TextFinished", "EditParticleEffectParticleSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("ParticleSizeMaxX", true), "TextFinished", "EditParticleEffectParticleSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("ParticleSizeMaxY", true), "TextFinished", "EditParticleEffectParticleSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("TimeToLiveMin", true), "TextFinished", "EditParticleEffectTimeToLive");
+    SubscribeToEvent(particleEffectWindow.GetChild("TimeToLiveMax", true), "TextFinished", "EditParticleEffectTimeToLive");
+    SubscribeToEvent(particleEffectWindow.GetChild("VelocityMin", true), "TextFinished", "EditParticleEffectVelocity");
+    SubscribeToEvent(particleEffectWindow.GetChild("VelocityMax", true), "TextFinished", "EditParticleEffectVelocity");
+    SubscribeToEvent(particleEffectWindow.GetChild("RotationMin", true), "TextFinished", "EditParticleEffectRotation");
+    SubscribeToEvent(particleEffectWindow.GetChild("RotationMax", true), "TextFinished", "EditParticleEffectRotation");
+    SubscribeToEvent(particleEffectWindow.GetChild("RotationSpeedMin", true), "TextFinished", "EditParticleEffectRotationSpeed");
+    SubscribeToEvent(particleEffectWindow.GetChild("RotationSpeedMax", true), "TextFinished", "EditParticleEffectRotationSpeed");
+    SubscribeToEvent(particleEffectWindow.GetChild("SizeAdd", true), "TextFinished", "EditParticleEffectSizeAdd");
+    SubscribeToEvent(particleEffectWindow.GetChild("SizeMultiply", true), "TextFinished", "EditParticleEffectSizeMultiply");
+    SubscribeToEvent(particleEffectWindow.GetChild("AnimationLodBias", true), "TextFinished", "EditParticleEffectAnimationLodBias");
+    SubscribeToEvent(particleEffectWindow.GetChild("NumParticles", true), "TextFinished", "EditParticleEffectNumParticles");
+    SubscribeToEvent(particleEffectWindow.GetChild("EmitterSizeX", true), "TextFinished", "EditParticleEffectEmitterSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("EmitterSizeY", true), "TextFinished", "EditParticleEffectEmitterSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("EmitterSizeZ", true), "TextFinished", "EditParticleEffectEmitterSize");
+    SubscribeToEvent(particleEffectWindow.GetChild("EmissionRateMin", true), "TextFinished", "EditParticleEffectEmissionRate");
+    SubscribeToEvent(particleEffectWindow.GetChild("EmissionRateMax", true), "TextFinished", "EditParticleEffectEmissionRate");
+
     SubscribeToEvent(particleEffectWindow.GetChild("EmitterShape", true), "ItemSelected", "EditParticleEffectEmitterShape");
     SubscribeToEvent(particleEffectWindow.GetChild("Scaled", true), "Toggled", "EditParticleEffectScaled");
     SubscribeToEvent(particleEffectWindow.GetChild("Sorted", true), "Toggled", "EditParticleEffectSorted");
@@ -87,9 +123,13 @@ void EditParticleEffectColorFrameNew(StringHash eventType, VariantMap& eventData
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     uint num = editParticleEffect.numColorFrames;
     editParticleEffect.numColorFrames = num + 1;
     RefreshParticleEffectColorFrames();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectTextureFrameNew(StringHash eventType, VariantMap& eventData)
@@ -97,22 +137,31 @@ void EditParticleEffectTextureFrameNew(StringHash eventType, VariantMap& eventDa
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     uint num = editParticleEffect.numTextureFrames;
     editParticleEffect.numTextureFrames = num + 1;
     RefreshParticleEffectTextureFrames();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectColorFrameRemove(StringHash eventType, VariantMap& eventData)
 {
     if (editParticleEffect is null)
         return;
-        
+
     ListView@ lv = particleEffectWindow.GetChild("ColorFrameListView", true);
     if (lv !is null && lv.selection != M_MAX_UNSIGNED )
     {
+        BeginParticleEffectEdit();
+        
         editParticleEffect.RemoveColorFrame(lv.selection);
         RefreshParticleEffectColorFrames();
+
+        EndParticleEffectEdit();
     }
+
 }
 
 
@@ -124,8 +173,12 @@ void EditParticleEffectTextureFrameRemove(StringHash eventType, VariantMap& even
     ListView@ lv = particleEffectWindow.GetChild("TextureFrameListView", true);
     if (lv !is null && lv.selection != M_MAX_UNSIGNED )
     {
+        BeginParticleEffectEdit();
+        
         editParticleEffect.RemoveTextureFrame(lv.selection);
         RefreshParticleEffectTextureFrames();
+
+        EndParticleEffectEdit();
     }
 }
 
@@ -195,6 +248,8 @@ void EditParticleEffectConstantForce(StringHash eventType, VariantMap& eventData
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     Vector3 v = editParticleEffect.constantForce;
@@ -207,6 +262,8 @@ void EditParticleEffectConstantForce(StringHash eventType, VariantMap& eventData
 
     if (element.name == "ConstantForceZ")
         editParticleEffect.constantForce = Vector3(v.x, v.y, element.text.ToFloat());
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectDirection(StringHash eventType, VariantMap& eventData)
@@ -214,6 +271,8 @@ void EditParticleEffectDirection(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     Vector3 vMin = editParticleEffect.minDirection;
@@ -236,6 +295,8 @@ void EditParticleEffectDirection(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "DirectionMaxZ")
         editParticleEffect.maxDirection = Vector3(vMax.x, vMax.y, element.text.ToFloat());
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectDampingForce(StringHash eventType, VariantMap& eventData)
@@ -243,9 +304,13 @@ void EditParticleEffectDampingForce(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.dampingForce = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectActiveTime(StringHash eventType, VariantMap& eventData)
@@ -256,10 +321,14 @@ void EditParticleEffectActiveTime(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.activeTime = element.text.ToFloat();
     particleEffectEmitter.Reset();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectInactiveTime(StringHash eventType, VariantMap& eventData)
@@ -269,11 +338,15 @@ void EditParticleEffectInactiveTime(StringHash eventType, VariantMap& eventData)
 
     if (particleEffectEmitter is null)
         return;
-        
+
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.inactiveTime = element.text.ToFloat();
     particleEffectEmitter.Reset();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectParticleSize(StringHash eventType, VariantMap& eventData)
@@ -281,6 +354,8 @@ void EditParticleEffectParticleSize(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     Vector2 vMin = editParticleEffect.minParticleSize;
@@ -297,6 +372,8 @@ void EditParticleEffectParticleSize(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "ParticleSizeMaxY")
         editParticleEffect.maxParticleSize = Vector2(vMax.x, element.text.ToFloat());
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectTimeToLive(StringHash eventType, VariantMap& eventData)
@@ -304,6 +381,8 @@ void EditParticleEffectTimeToLive(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     float vMin = editParticleEffect.minTimeToLive;
@@ -314,6 +393,8 @@ void EditParticleEffectTimeToLive(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "TimeToLiveMax")
         editParticleEffect.maxTimeToLive = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectVelocity(StringHash eventType, VariantMap& eventData)
@@ -321,6 +402,8 @@ void EditParticleEffectVelocity(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     float vMin = editParticleEffect.minVelocity;
@@ -331,6 +414,8 @@ void EditParticleEffectVelocity(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "VelocityMax")
         editParticleEffect.maxVelocity = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectRotation(StringHash eventType, VariantMap& eventData)
@@ -338,6 +423,8 @@ void EditParticleEffectRotation(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     float vMin = editParticleEffect.minRotation;
@@ -348,6 +435,8 @@ void EditParticleEffectRotation(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "RotationMax")
         editParticleEffect.maxRotation = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectRotationSpeed(StringHash eventType, VariantMap& eventData)
@@ -355,6 +444,8 @@ void EditParticleEffectRotationSpeed(StringHash eventType, VariantMap& eventData
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     float vMin = editParticleEffect.minRotationSpeed;
@@ -365,6 +456,8 @@ void EditParticleEffectRotationSpeed(StringHash eventType, VariantMap& eventData
 
     if (element.name == "RotationSpeedMax")
         editParticleEffect.maxRotationSpeed = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectSizeAdd(StringHash eventType, VariantMap& eventData)
@@ -372,9 +465,13 @@ void EditParticleEffectSizeAdd(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.sizeAdd = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectSizeMultiply(StringHash eventType, VariantMap& eventData)
@@ -382,9 +479,13 @@ void EditParticleEffectSizeMultiply(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.sizeMul = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectAnimationLodBias(StringHash eventType, VariantMap& eventData)
@@ -392,9 +493,13 @@ void EditParticleEffectAnimationLodBias(StringHash eventType, VariantMap& eventD
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.animationLodBias = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectNumParticles(StringHash eventType, VariantMap& eventData)
@@ -405,10 +510,14 @@ void EditParticleEffectNumParticles(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.numParticles = element.text.ToInt();
     particleEffectEmitter.ApplyEffect();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectEmitterSize(StringHash eventType, VariantMap& eventData)
@@ -416,6 +525,8 @@ void EditParticleEffectEmitterSize(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     Vector3 v = editParticleEffect.emitterSize;
@@ -428,6 +539,8 @@ void EditParticleEffectEmitterSize(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "EmitterSizeZ")
         editParticleEffect.emitterSize = Vector3(v.x, v.y, element.text.ToFloat());
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectEmissionRate(StringHash eventType, VariantMap& eventData)
@@ -435,6 +548,8 @@ void EditParticleEffectEmissionRate(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
 
     if (element.name == "EmissionRateMin")
@@ -442,6 +557,8 @@ void EditParticleEffectEmissionRate(StringHash eventType, VariantMap& eventData)
 
     if (element.name == "EmissionRateMax")
         editParticleEffect.maxEmissionRate = element.text.ToFloat();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectEmitterShape(StringHash eventType, VariantMap& eventData)
@@ -449,6 +566,8 @@ void EditParticleEffectEmitterShape(StringHash eventType, VariantMap& eventData)
     if (editParticleEffect is null)
         return;
 
+    BeginParticleEffectEdit();
+
     DropDownList@ element = eventData["Element"].GetPtr();
 
     switch (element.selection)
@@ -461,6 +580,8 @@ void EditParticleEffectEmitterShape(StringHash eventType, VariantMap& eventData)
             editParticleEffect.emitterType = EMITTER_SPHERE;
             break;
     }
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectMaterial(StringHash eventType, VariantMap& eventData)
@@ -476,8 +597,12 @@ void EditParticleEffectMaterial(StringHash eventType, VariantMap& eventData)
 
     if (res !is null)
     {
+        BeginParticleEffectEdit();
+
         editParticleEffect.material = res;
         particleEffectEmitter.ApplyEffect();
+        
+        EndParticleEffectEdit();
     }
 }
 
@@ -526,10 +651,14 @@ void EditParticleEffectScaled(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     CheckBox@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.scaled = element.checked;
     particleEffectEmitter.ApplyEffect();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectSorted(StringHash eventType, VariantMap& eventData)
@@ -540,10 +669,14 @@ void EditParticleEffectSorted(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     CheckBox@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.sorted = element.checked;
     particleEffectEmitter.ApplyEffect();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectRelative(StringHash eventType, VariantMap& eventData)
@@ -553,11 +686,15 @@ void EditParticleEffectRelative(StringHash eventType, VariantMap& eventData)
 
     if (particleEffectEmitter is null)
         return;
-        
+
+    BeginParticleEffectEdit();
+
     CheckBox@ element = eventData["Element"].GetPtr();
 
     editParticleEffect.relative = element.checked;
     particleEffectEmitter.ApplyEffect();
+
+    EndParticleEffectEdit();
 }
 
 bool ShowParticleEffectEditor()
@@ -638,7 +775,9 @@ void EditParticleEffect(ParticleEffect@ effect)
     if (editParticleEffect !is null)
         UnsubscribeFromEvent(editParticleEffect, "ReloadFinished");
 
-    cache.ReloadResource(effect);
+    if (!effect.name.empty)
+        cache.ReloadResource(effect);
+        
     editParticleEffect = effect;
     particleEffectEmitter.effect = editParticleEffect;
 
@@ -905,6 +1044,8 @@ void EditParticleEffectColorFrame(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
     uint i = element.vars["ColorFrame"].GetInt();
     ColorFrame@ cf = editParticleEffect.GetColorFrame(i);
@@ -926,6 +1067,8 @@ void EditParticleEffectColorFrame(StringHash eventType, VariantMap& eventData)
 
     editParticleEffect.SetColorFrame(i, cf);
     particleEffectEmitter.Reset();
+
+    EndParticleEffectEdit();
 }
 
 void EditParticleEffectTextureFrame(StringHash eventType, VariantMap& eventData)
@@ -936,6 +1079,8 @@ void EditParticleEffectTextureFrame(StringHash eventType, VariantMap& eventData)
     if (particleEffectEmitter is null)
         return;
 
+    BeginParticleEffectEdit();
+
     LineEdit@ element = eventData["Element"].GetPtr();
     uint i = element.vars["TextureFrame"].GetInt();
     TextureFrame@ tf = editParticleEffect.GetTextureFrame(i);
@@ -957,6 +1102,8 @@ void EditParticleEffectTextureFrame(StringHash eventType, VariantMap& eventData)
 
     editParticleEffect.SetTextureFrame(i, tf);
     particleEffectEmitter.Reset();
+
+    EndParticleEffectEdit();
 }
 
 void RefreshParticleEffectPreview()
@@ -1142,7 +1289,11 @@ void PickEditParticleEffectDone(StringHash eventType, VariantMap& eventData)
 
 void NewParticleEffect()
 {
+    BeginParticleEffectEdit();
+
     EditParticleEffect(CreateNewParticleEffect());
+    
+    EndParticleEffectEdit();
 }
 
 void RevertParticleEffect()
@@ -1150,8 +1301,16 @@ void RevertParticleEffect()
     if (editParticleEffect is null)
         return;
 
+    if (editParticleEffect.name.empty)
+    {
+        NewParticleEffect();
+        return;
+    }
+
     BeginParticleEffectEdit();
+    
     cache.ReloadResource(editParticleEffect);
+
     EndParticleEffectEdit();
     
     RefreshParticleEffectEditor();
@@ -1226,18 +1385,19 @@ void BeginParticleEffectEdit()
         return;
 
     oldParticleEffectState = XMLFile();
-    XMLElement particleEffectElem = oldParticleEffectState.CreateRoot("particleemitter");
-    //editParticleEffect.Save(particleEffectElem);
+    XMLElement particleElem = oldParticleEffectState.CreateRoot("particleeffect");
+    editParticleEffect.Save(particleElem);
 }
 
 void EndParticleEffectEdit()
 {
     if (editParticleEffect is null)
         return;
+        
     if (!dragEditAttribute)
     {
         EditParticleEffectAction@ action = EditParticleEffectAction();
-        action.Define(editParticleEffect, oldParticleEffectState);
+        action.Define(particleEffectEmitter, editParticleEffect, oldParticleEffectState);
         SaveEditAction(action);
     }
     

+ 216 - 27
Source/Engine/Graphics/ParticleEffect.cpp

@@ -292,85 +292,274 @@ bool ParticleEffect::EndLoad()
     return true;
 }
 
+bool ParticleEffect::Load(const XMLElement& source)
+{
+    // Reset to defaults first so that missing parameters in case of a live reload behave as expected
+    material_.Reset();
+    numParticles_ = DEFAULT_NUM_PARTICLES;
+    updateInvisible_ = false;
+    relative_ = true;
+    scaled_ = true;
+    sorted_ = false;
+    animationLodBias_ = 0.0f;
+    emitterType_ = EMITTER_SPHERE;
+    emitterSize_ = Vector3::ZERO;
+    directionMin_ = DEFAULT_DIRECTION_MIN;
+    directionMax_ = DEFAULT_DIRECTION_MAX;
+    constantForce_ = Vector3::ZERO;
+    dampingForce_ = 0.0f;
+    activeTime_ = 0.0f;
+    inactiveTime_ = 0.0;
+    emissionRateMin_ = DEFAULT_EMISSION_RATE;
+    emissionRateMax_ = DEFAULT_EMISSION_RATE;
+    sizeMin_ = DEFAULT_PARTICLE_SIZE;
+    sizeMax_ = DEFAULT_PARTICLE_SIZE;
+    timeToLiveMin_ = DEFAULT_TIME_TO_LIVE;
+    timeToLiveMax_ = DEFAULT_TIME_TO_LIVE;
+    velocityMin_ = DEFAULT_VELOCITY;
+    velocityMax_ = DEFAULT_VELOCITY;
+    rotationMin_ = 0.0f;
+    rotationMax_ = 0.0f;
+    rotationSpeedMin_ = 0.0f;
+    rotationSpeedMax_ = 0.0f;
+    sizeAdd_ = 0.0f;
+    sizeMul_ = 1.0f;
+    colorFrames_.Clear();
+    textureFrames_.Clear();
+
+    if (source.IsNull())
+    {
+        LOGERROR("Can not load particle effect from null XML element");
+        return false;
+    }
+
+    if (source.HasChild("material"))
+    {
+        material_ = GetSubsystem<ResourceCache>()->GetResource<Material>(source.GetChild("material").GetAttribute("name"));
+    }
+
+    if (source.HasChild("numparticles"))
+        SetNumParticles(source.GetChild("numparticles").GetInt("value"));
+
+    if (source.HasChild("updateinvisible"))
+        updateInvisible_ = source.GetChild("updateinvisible").GetBool("enable");
+
+    if (source.HasChild("relative"))
+        relative_ = source.GetChild("relative").GetBool("enable");
+
+    if (source.HasChild("scaled"))
+        scaled_ = source.GetChild("scaled").GetBool("enable");
+
+    if (source.HasChild("sorted"))
+        sorted_ = source.GetChild("sorted").GetBool("enable");
+
+    if (source.HasChild("animlodbias"))
+        SetAnimationLodBias(source.GetChild("animlodbias").GetFloat("value"));
+
+    if (source.HasChild("emittertype"))
+    {
+        String type = source.GetChild("emittertype").GetAttributeLower("value");
+        if (type == "point")
+        {
+            // Point emitter type is deprecated, handled as zero sized sphere
+            emitterType_ = EMITTER_SPHERE;
+            emitterSize_ = Vector3::ZERO;
+        }
+        else if (type == "box")
+            emitterType_ = EMITTER_BOX;
+        else if (type == "sphere")
+            emitterType_ = EMITTER_SPHERE;
+        else
+            LOGERROR("Unknown particle emitter type " + type);
+    }
+
+    if (source.HasChild("emittersize"))
+        emitterSize_ = source.GetChild("emittersize").GetVector3("value");
+
+    if (source.HasChild("emitterradius"))
+        emitterSize_.x_ = emitterSize_.y_ = emitterSize_.z_ = source.GetChild("emitterradius").GetFloat("value");
+
+    if (source.HasChild("direction"))
+        GetVector3MinMax(source.GetChild("direction"), directionMin_, directionMax_);
+
+    if (source.HasChild("constantforce"))
+        constantForce_ = source.GetChild("constantforce").GetVector3("value");
+
+    if (source.HasChild("dampingforce"))
+        dampingForce_ = source.GetChild("dampingforce").GetFloat("value");
+
+    if (source.HasChild("activetime"))
+        activeTime_ = source.GetChild("activetime").GetFloat("value");
+    if (activeTime_ < 0.0f)
+        activeTime_ = M_INFINITY;
+
+    if (source.HasChild("inactivetime"))
+        inactiveTime_ = source.GetChild("inactivetime").GetFloat("value");
+    if (inactiveTime_ < 0.0f)
+        inactiveTime_ = M_INFINITY;
+
+    if (source.HasChild("emissionrate"))
+        GetFloatMinMax(source.GetChild("emissionrate"), emissionRateMin_, emissionRateMax_);
+
+    if (source.HasChild("interval"))
+    {
+        float intervalMin = 0.0f;
+        float intervalMax = 0.0f;
+        GetFloatMinMax(source.GetChild("interval"), intervalMin, intervalMax);
+        emissionRateMax_ = 1.0f / intervalMin;
+        emissionRateMin_ = 1.0f / intervalMax;
+    }
+
+    if (source.HasChild("particlesize"))
+        GetVector2MinMax(source.GetChild("particlesize"), sizeMin_, sizeMax_);
+
+    if (source.HasChild("timetolive"))
+        GetFloatMinMax(source.GetChild("timetolive"), timeToLiveMin_, timeToLiveMax_);
+
+    if (source.HasChild("velocity"))
+        GetFloatMinMax(source.GetChild("velocity"), velocityMin_, velocityMax_);
+
+    if (source.HasChild("rotation"))
+        GetFloatMinMax(source.GetChild("rotation"), rotationMin_, rotationMax_);
+
+    if (source.HasChild("rotationspeed"))
+        GetFloatMinMax(source.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
+
+    if (source.HasChild("sizedelta"))
+    {
+        XMLElement deltaElem = source.GetChild("sizedelta");
+        if (deltaElem.HasAttribute("add"))
+            sizeAdd_ = deltaElem.GetFloat("add");
+        if (deltaElem.HasAttribute("mul"))
+            sizeMul_ = deltaElem.GetFloat("mul");
+    }
+
+    if (source.HasChild("color"))
+    {
+        ColorFrame colorFrame(source.GetChild("color").GetColor("value"));
+        SetColorFrame(0, colorFrame);
+    }
+
+    if (source.HasChild("colorfade"))
+    {
+        Vector<ColorFrame> fades;
+        for (XMLElement colorFadeElem = source.GetChild("colorfade"); colorFadeElem; colorFadeElem = colorFadeElem.GetNext("colorfade"))
+            fades.Push(ColorFrame(colorFadeElem.GetColor("color"), colorFadeElem.GetFloat("time")));
+
+        SetColorFrames(fades);
+    }
+
+    if (colorFrames_.Empty())
+        colorFrames_.Push(ColorFrame(Color::WHITE));
+
+    if (source.HasChild("texanim"))
+    {
+        Vector<TextureFrame> animations;
+        for (XMLElement animElem = source.GetChild("texanim"); animElem; animElem = animElem.GetNext("texanim"))
+        {
+            TextureFrame animation;
+            animation.uv_ = animElem.GetRect("uv");
+            animation.time_ = animElem.GetFloat("time");
+            animations.Push(animation);
+        }
+
+        SetTextureFrames(animations);
+    }
+
+    return true;
+}
+
 bool ParticleEffect::Save(Serializer& dest) const
 {
-    XMLFile file(context_);
-    XMLElement rootElem = file.CreateRoot("particleeffect");
+    SharedPtr<XMLFile> xml(new XMLFile(context_));
+    XMLElement materialElem = xml->CreateRoot("particleeffect");
 
-    XMLElement childElem = rootElem.CreateChild("material");
+    Save(materialElem);
+    return xml->Save(dest);
+}
+
+bool ParticleEffect::Save(XMLElement& dest) const
+{
+    if (dest.IsNull())
+    {
+        LOGERROR("Can not save particle effect to null XML element");
+        return false;
+    }
+
+    XMLElement childElem = dest.CreateChild("material");
     childElem.SetAttribute("name", GetResourceName(material_));
 
-    childElem = rootElem.CreateChild("numparticles");
+    childElem = dest.CreateChild("numparticles");
     childElem.SetInt("value", numParticles_);
 
-    childElem = rootElem.CreateChild("updateinvisible");
+    childElem = dest.CreateChild("updateinvisible");
     childElem.SetBool("enable", updateInvisible_);
 
-    childElem = rootElem.CreateChild("relative");
+    childElem = dest.CreateChild("relative");
     childElem.SetBool("enable", relative_);
 
-    childElem = rootElem.CreateChild("scaled");
+    childElem = dest.CreateChild("scaled");
     childElem.SetBool("enable", scaled_);
 
-    childElem = rootElem.CreateChild("sorted");
+    childElem = dest.CreateChild("sorted");
     childElem.SetBool("enable", sorted_);
 
-    childElem = rootElem.CreateChild("animlodbias");
+    childElem = dest.CreateChild("animlodbias");
     childElem.SetFloat("value", animationLodBias_);
 
-    childElem = rootElem.CreateChild("emittertype");
+    childElem = dest.CreateChild("emittertype");
     childElem.SetAttribute("value", emitterTypeNames[emitterType_]);
 
-    childElem = rootElem.CreateChild("emittersize");
+    childElem = dest.CreateChild("emittersize");
     childElem.SetVector3("value", emitterSize_);
 
-    childElem = rootElem.CreateChild("direction");
+    childElem = dest.CreateChild("direction");
     childElem.SetVector3("min", directionMin_);
     childElem.SetVector3("max", directionMax_);
 
-    childElem = rootElem.CreateChild("constantforce");
+    childElem = dest.CreateChild("constantforce");
     childElem.SetVector3("value", constantForce_);
 
-    childElem = rootElem.CreateChild("dampingforce");
+    childElem = dest.CreateChild("dampingforce");
     childElem.SetFloat("value", dampingForce_);
 
-    childElem = rootElem.CreateChild("activetime");
+    childElem = dest.CreateChild("activetime");
     childElem.SetFloat("value", activeTime_);
 
-    childElem = rootElem.CreateChild("inactivetime");
+    childElem = dest.CreateChild("inactivetime");
     childElem.SetFloat("value", inactiveTime_);
 
-    childElem = rootElem.CreateChild("emissionrate");
+    childElem = dest.CreateChild("emissionrate");
     childElem.SetFloat("min", emissionRateMin_);
     childElem.SetFloat("max", emissionRateMax_);
 
-    childElem = rootElem.CreateChild("particlesize");
+    childElem = dest.CreateChild("particlesize");
     childElem.SetVector2("min", sizeMin_);
     childElem.SetVector2("max", sizeMax_);
 
-    childElem = rootElem.CreateChild("timetolive");
+    childElem = dest.CreateChild("timetolive");
     childElem.SetFloat("min", timeToLiveMin_);
     childElem.SetFloat("max", timeToLiveMax_);
 
-    childElem = rootElem.CreateChild("velocity");
+    childElem = dest.CreateChild("velocity");
     childElem.SetFloat("min", velocityMin_);
     childElem.SetFloat("max", velocityMax_);
 
-    childElem = rootElem.CreateChild("rotation");
+    childElem = dest.CreateChild("rotation");
     childElem.SetFloat("min", rotationMin_);
     childElem.SetFloat("max", rotationMax_);
 
-    childElem = rootElem.CreateChild("rotationspeed");
+    childElem = dest.CreateChild("rotationspeed");
     childElem.SetFloat("min", rotationSpeedMin_);
     childElem.SetFloat("max", rotationSpeedMax_);
 
-    childElem = rootElem.CreateChild("sizedelta");
+    childElem = dest.CreateChild("sizedelta");
     childElem.SetFloat("add", sizeAdd_);
     childElem.SetFloat("mul", sizeMul_);
 
     if (colorFrames_.Size() == 1)
     {
-        childElem = rootElem.CreateChild("color");
+        childElem = dest.CreateChild("color");
         childElem.SetColor("value", colorFrames_[0].color_);
     }
 
@@ -378,7 +567,7 @@ bool ParticleEffect::Save(Serializer& dest) const
     {
         for (unsigned i = 0; i < colorFrames_.Size(); ++i)
         {
-            childElem = rootElem.CreateChild("colorfade");
+            childElem = dest.CreateChild("colorfade");
             childElem.SetColor("color", colorFrames_[i].color_);
             childElem.SetFloat("time", colorFrames_[i].time_);
         }
@@ -386,12 +575,12 @@ bool ParticleEffect::Save(Serializer& dest) const
 
     for (unsigned i = 0; i < textureFrames_.Size(); ++i)
     {
-        childElem = rootElem.CreateChild("texanim");
+        childElem = dest.CreateChild("texanim");
         childElem.SetRect("uv", textureFrames_[i].uv_);
         childElem.SetFloat("time", textureFrames_[i].time_);
     }
 
-    return file.Save(dest);
+    return true;
 }
 
 void ParticleEffect::SetMaterial(Material* material)

+ 4 - 0
Source/Engine/Graphics/ParticleEffect.h

@@ -117,6 +117,10 @@ public:
     virtual bool EndLoad();
     /// Save resource. Return true if successful.
     virtual bool Save(Serializer& dest) const;
+    /// Save resource to XMLElement. Return true if successful.
+    virtual bool Save(XMLElement& dest) const;
+    /// Load resource from XMLElement synchronously. Return true if successful.
+    virtual bool Load(const XMLElement& source);
 
     /// Set material.
     void SetMaterial(Material* material);

+ 2 - 1
Source/Engine/Script/GraphicsAPI.cpp

@@ -1085,7 +1085,8 @@ static void RegisterParticleEffect(asIScriptEngine* engine)
     engine->RegisterObjectProperty("TextureFrame", "float time", offsetof(TextureFrame, time_));
     
     RegisterResource<ParticleEffect>(engine, "ParticleEffect");
-
+    engine->RegisterObjectMethod("ParticleEffect", "bool Load(const XMLElement&in)", asMETHODPR(ParticleEffect, Load, (const XMLElement&), bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ParticleEffect", "bool Save(XMLElement&in) const", asMETHODPR(ParticleEffect, Save, (XMLElement&) const, bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEffect", "void set_material(Material@+)", asMETHOD(ParticleEffect, SetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEffect", "Material@+ get_material() const", asMETHOD(ParticleEffect, GetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEffect", "void set_numParticles(uint) const", asMETHOD(ParticleEffect, SetNumParticles), asCALL_THISCALL);