Browse Source

Group components into categories. In the Editor app, dynamically create the menu structure based on the component categories. Added new GetCategory() method in Component class and exposed it to script. Reviewed and fixed as necessary the default attributes registration of Component's descendant classes. Removed 'Attenuation' and 'Panning' attributes from SoundSource3D as they are updated constantly in this class. Enhanced Context class to 'register' component category during the component factory registration.

Wei Tjong Yao 12 years ago
parent
commit
8d1c28bc3e
50 changed files with 775 additions and 627 deletions
  1. 13 3
      Bin/Data/Scripts/Editor/EditorUI.as
  2. 33 1
      Docs/ScriptAPI.dox
  3. 2 0
      Engine/Audio/Audio.cpp
  4. 9 7
      Engine/Audio/Audio.h
  5. 4 2
      Engine/Audio/SoundListener.cpp
  6. 73 73
      Engine/Audio/SoundSource.cpp
  7. 8 5
      Engine/Audio/SoundSource3D.cpp
  8. 6 0
      Engine/Core/Context.cpp
  9. 18 9
      Engine/Core/Context.h
  10. 1 0
      Engine/Engine/APITemplates.h
  11. 32 18
      Engine/Engine/SceneAPI.cpp
  12. 1 1
      Engine/Graphics/AnimatedModel.cpp
  13. 2 0
      Engine/Graphics/Animation.cpp
  14. 2 0
      Engine/Graphics/Animation.h
  15. 1 1
      Engine/Graphics/AnimationController.cpp
  16. 3 1
      Engine/Graphics/BillboardSet.cpp
  17. 31 29
      Engine/Graphics/Camera.cpp
  18. 3 1
      Engine/Graphics/CustomGeometry.cpp
  19. 48 46
      Engine/Graphics/DebugRenderer.cpp
  20. 1 1
      Engine/Graphics/DecalSet.cpp
  21. 28 26
      Engine/Graphics/Light.cpp
  22. 67 67
      Engine/Graphics/Octree.cpp
  23. 1 1
      Engine/Graphics/ParticleEmitter.cpp
  24. 6 4
      Engine/Graphics/Skybox.cpp
  25. 3 1
      Engine/Graphics/StaticModel.cpp
  26. 92 90
      Engine/Graphics/Terrain.cpp
  27. 9 7
      Engine/Graphics/Terrain.h
  28. 1 1
      Engine/Graphics/TerrainPatch.cpp
  29. 1 1
      Engine/Graphics/Zone.cpp
  30. 4 2
      Engine/Navigation/Navigable.cpp
  31. 3 3
      Engine/Navigation/Navigable.h
  32. 2 0
      Engine/Navigation/Navigation.cpp
  33. 2 0
      Engine/Navigation/Navigation.h
  34. 3 1
      Engine/Navigation/NavigationMesh.cpp
  35. 3 1
      Engine/Navigation/OffMeshConnection.cpp
  36. 3 1
      Engine/Network/NetworkPriority.cpp
  37. 1 1
      Engine/Physics/CollisionShape.cpp
  38. 1 1
      Engine/Physics/Constraint.cpp
  39. 86 84
      Engine/Physics/PhysicsWorld.cpp
  40. 12 10
      Engine/Physics/PhysicsWorld.h
  41. 47 47
      Engine/Physics/RigidBody.cpp
  42. 27 15
      Engine/Scene/Component.cpp
  43. 3 0
      Engine/Scene/Component.h
  44. 3 0
      Engine/Scene/Scene.cpp
  45. 3 0
      Engine/Scene/Scene.h
  46. 14 12
      Engine/Scene/SmoothedTransform.cpp
  47. 47 45
      Engine/Script/Script.cpp
  48. 8 6
      Engine/Script/Script.h
  49. 1 1
      Engine/Script/ScriptInstance.cpp
  50. 3 1
      Engine/UI/Text3D.cpp

+ 13 - 3
Bin/Data/Scripts/Editor/EditorUI.as

@@ -192,9 +192,17 @@ void CreateMenuBar()
 
         Menu@ childMenu = CreateMenuItem("Component", null, SHOW_POPUP_INDICATOR);
         Window@ childPopup = CreatePopup(childMenu);
-        String[] componentTypes = GetAvailableComponents();
-        for (uint i = 0; i < componentTypes.length; ++i)
-            childPopup.AddChild(CreateIconizedMenuItem(componentTypes[i], @PickComponent));
+        String[] componentCategories = GetComponentCategories();
+        for (uint i = 0; i < componentCategories.length; ++i)
+        {
+            Menu@ menu = CreateMenuItem(componentCategories[i], null, SHOW_POPUP_INDICATOR);
+            Window@ popup = CreatePopup(menu);
+            String[] componentTypes = GetComponentsByCategory(componentCategories[i]);
+            for (uint j = 0; j < componentTypes.length; ++j)
+                popup.AddChild(CreateIconizedMenuItem(componentTypes[j], @PickComponent));
+            childPopup.AddChild(menu);
+        }
+        FinalizedPopupMenu(childPopup);
         popup.AddChild(childMenu);
 
         childMenu = CreateMenuItem("Builtin object", null, SHOW_POPUP_INDICATOR);
@@ -467,8 +475,10 @@ Menu@ CreateMenuItem(const String&in title, MENU_CALLBACK@ callback = null, int
 
     if (accelKey != 0)
     {
+        int minWidth = menuText.minWidth;
         menuText.layoutMode = LM_HORIZONTAL;
         menuText.AddChild(CreateAccelKeyText(accelKey, accelQual));
+        menuText.minWidth = minWidth;
     }
 
     return menu;

+ 33 - 1
Docs/ScriptAPI.dox

@@ -66,7 +66,8 @@ namespace Urho3D
 - String RemoveTrailingSlash(const String&)
 - String GetParentPath(const String&)
 - String GetInternalPath(const String&)
-- String[]@ GetAvailableComponents()
+- String[]@ GetComponentCategories()
+- String[]@ GetComponentsByCategory(const String&)
 - uint GetAlphaFormat()
 - uint GetLuminanceFormat()
 - uint GetLuminanceAlphaFormat()
@@ -1375,6 +1376,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 
 
@@ -1503,6 +1505,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - Vector3 targetPosition
 - Quaternion targetRotation
@@ -1688,6 +1691,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 
 
@@ -1728,6 +1732,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - float nearClip
 - float farClip
@@ -2117,6 +2122,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2184,6 +2190,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2248,6 +2255,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2305,6 +2313,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2358,6 +2367,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2440,6 +2450,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2526,6 +2537,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 
 
@@ -2571,6 +2583,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2626,6 +2639,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2689,6 +2703,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2743,6 +2758,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2796,6 +2812,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -2845,6 +2862,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - Material@ material
 - Image@ heightMap
@@ -2915,6 +2933,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - BoundingBox worldBoundingBox (readonly)
 - uint numLevels (readonly)
@@ -3114,6 +3133,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 
 
@@ -3152,6 +3172,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - SoundType soundType
 - float frequency
@@ -3200,6 +3221,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - SoundType soundType
 - float frequency
@@ -4741,6 +4763,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool inView (readonly)
 - bool castShadows
@@ -5455,6 +5478,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - float basePriority
 - float distanceFactor
@@ -5561,6 +5585,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - ShapeType shapeType
 - Vector3 size
@@ -5612,6 +5637,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - float mass
 - Vector3 position
@@ -5670,6 +5696,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - ConstraintType constraintType
 - Vector3 position
@@ -5734,6 +5761,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - Vector3 gravity
 - int fps
@@ -5770,6 +5798,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - bool recursive
 
@@ -5811,6 +5840,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - int tileSize
 - float cellSize
@@ -5862,6 +5892,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - Node@ endPoint
 - float radius
@@ -5926,6 +5957,7 @@ Properties:<br>
 - bool enabled
 - bool enabledEffective (readonly)
 - uint id (readonly)
+- String category (readonly)
 - Node@ node (readonly)
 - int fixedUpdateFps
 - ScriptFile@ scriptFile

+ 2 - 0
Engine/Audio/Audio.cpp

@@ -39,6 +39,8 @@
 namespace Urho3D
 {
 
+const char* AUDIO_CATEGORY = "Audio";
+
 static const int MIN_BUFFERLENGTH = 20;
 static const int MIN_MIXRATE = 11025;
 static const int MAX_MIXRATE = 48000;

+ 9 - 7
Engine/Audio/Audio.h

@@ -30,6 +30,8 @@
 namespace Urho3D
 {
 
+extern const char* AUDIO_CATEGORY;
+
 class AudioImpl;
 class Sound;
 class SoundListener;
@@ -39,13 +41,13 @@ class SoundSource;
 class Audio : public Object
 {
     OBJECT(Audio);
-    
+
 public:
     /// Construct.
     Audio(Context* context);
     /// Destruct. Terminate the audio thread and free the audio buffer.
     virtual ~Audio();
-    
+
     /// Initialize sound output with specified buffer length and output mode.
     bool SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpolation = true);
     /// Run update on sound sources. Not required for continued playback, but frees unused sound sources & sounds and updates 3D positions.
@@ -60,7 +62,7 @@ public:
     void SetListener(SoundListener* listener);
     /// Stop any sound source playing a certain sound clip.
     void StopSound(Sound* sound);
-    
+
     /// Return byte size of one sample.
     unsigned GetSampleSize() const { return sampleSize_; }
     /// Return mixing rate.
@@ -79,7 +81,7 @@ public:
     SoundListener* GetListener() const;
     /// Return all sound sources.
     const PODVector<SoundSource*>& GetSoundSources() const { return soundSources_; }
-    
+
     /// Add a sound source to keep track of. Called by SoundSource.
     void AddSoundSource(SoundSource* soundSource);
     /// Remove a sound source. Called by SoundSource.
@@ -88,16 +90,16 @@ public:
     Mutex& GetMutex() { return audioMutex_; }
     /// Return sound type specific gain multiplied by master gain.
     float GetSoundSourceMasterGain(SoundType type) const { return masterGain_[SOUND_MASTER] * masterGain_[type]; }
-    
+
     /// Mix sound sources into the buffer.
     void MixOutput(void *dest, unsigned samples);
-    
+
 private:
     /// Handle render update event.
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     /// Stop sound output and release the sound buffer.
     void Release();
-    
+
     /// Clipping buffer for mixing.
     SharedArrayPtr<int> clipBuffer_;
     /// Audio thread mutex.

+ 4 - 2
Engine/Audio/SoundListener.cpp

@@ -29,6 +29,8 @@
 namespace Urho3D
 {
 
+extern const char* AUDIO_CATEGORY;
+
 OBJECTTYPESTATIC(SoundListener);
 
 SoundListener::SoundListener(Context* context) :
@@ -42,8 +44,8 @@ SoundListener::~SoundListener()
 
 void SoundListener::RegisterObject(Context* context)
 {
-    context->RegisterFactory<SoundListener>();
-    
+    context->RegisterComponentFactory<SoundListener>(AUDIO_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(SoundListener, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
 }
 

+ 73 - 73
Engine/Audio/SoundSource.cpp

@@ -119,7 +119,7 @@ SoundSource::SoundSource(Context* context) :
     decodePosition_(0)
 {
     audio_ = GetSubsystem<Audio>();
-    
+
     if (audio_)
         audio_->AddSoundSource(this);
 }
@@ -128,14 +128,14 @@ SoundSource::~SoundSource()
 {
     if (audio_)
         audio_->RemoveSoundSource(this);
-    
+
     FreeDecoder();
 }
 
 void SoundSource::RegisterObject(Context* context)
 {
-    context->RegisterFactory<SoundSource>();
-    
+    context->RegisterComponentFactory<SoundSource>(AUDIO_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(SoundSource, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ENUM_ATTRIBUTE(SoundSource, "Sound Type", soundType_, typeNames, SOUND_EFFECT, AM_DEFAULT);
     ATTRIBUTE(SoundSource, VAR_FLOAT, "Frequency", frequency_, 0.0f, AM_DEFAULT);
@@ -151,11 +151,11 @@ void SoundSource::Play(Sound* sound)
 {
     if (!audio_)
         return;
-    
+
     // If no frequency set yet, set from the sound's default
     if (frequency_ == 0.0f && sound)
         SetFrequency(sound->GetFrequency());
-    
+
     // If sound source is currently playing, have to lock the audio mutex
     if (position_)
     {
@@ -164,7 +164,7 @@ void SoundSource::Play(Sound* sound)
     }
     else
         PlayLockless(sound);
-    
+
     MarkNetworkUpdate();
 }
 
@@ -193,18 +193,18 @@ void SoundSource::Stop()
 {
     if (!audio_)
         return;
-    
+
     // If sound source is currently playing, have to lock the audio mutex
     if (position_)
     {
         MutexLock lock(audio_->GetMutex());
         StopLockless();
     }
-    
+
     // Free the compressed sound decoder now if any
     FreeDecoder();
     sound_.Reset();
-    
+
     MarkNetworkUpdate();
 }
 
@@ -212,7 +212,7 @@ void SoundSource::SetSoundType(SoundType type)
 {
     if (type == SOUND_MASTER || type >= MAX_SOUND_TYPES)
         return;
-    
+
     soundType_ = type;
     MarkNetworkUpdate();
 }
@@ -255,7 +255,7 @@ void SoundSource::SetPlayPosition(signed char* pos)
 {
     if (!audio_ || !sound_)
         return;
-    
+
     MutexLock lock(audio_->GetMutex());
     SetPlayPositionLockless(pos);
 }
@@ -264,7 +264,7 @@ void SoundSource::PlayLockless(Sound* sound)
 {
     // Reset the time position in any case
     timePosition_ = 0.0f;
-    
+
     if (sound)
     {
         if (!sound->IsCompressed())
@@ -300,7 +300,7 @@ void SoundSource::PlayLockless(Sound* sound)
             }
         }
     }
-    
+
     // If sound pointer is null or if sound has no data, stop playback
     FreeDecoder();
     sound_.Reset();
@@ -318,7 +318,7 @@ void SoundSource::SetPlayPositionLockless(signed char* pos)
     // Setting position on a compressed sound is not supported
     if (!sound_ || sound_->IsCompressed())
         return;
-    
+
     signed char* start = sound_->GetStart();
     signed char* end = sound_->GetEnd();
     if (pos < start)
@@ -327,7 +327,7 @@ void SoundSource::SetPlayPositionLockless(signed char* pos)
         ++pos;
     if (pos > end)
         pos = end;
-    
+
     position_ = pos;
     timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
 }
@@ -336,18 +336,18 @@ void SoundSource::Update(float timeStep)
 {
     if (!audio_ || !IsEnabledEffective())
         return;
-    
+
     // If there is no actual audio output, perform fake mixing into a nonexistent buffer to check stopping/looping
     if (!audio_->IsInitialized())
         MixNull(timeStep);
-    
+
     // Free the sound if playback has stopped
     if (sound_ && !position_)
     {
         FreeDecoder();
         sound_.Reset();
     }
-    
+
     // Check for autoremove
     if (autoRemove_)
     {
@@ -370,7 +370,7 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
 {
     if (!position_ || !sound_ || !IsEnabledEffective())
         return;
-    
+
     if (sound_->IsCompressed())
     {
         if (decoder_)
@@ -404,13 +404,13 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
                         if (sound_->IsLooped())
                             eof = true;
                     }
-                    
+
                     // If wrote to buffer start, correct interpolation wraparound
                     if (!decodePosition_)
                         decodeBuffer_->FixInterpolation();
                 }
             }
-            
+
             // If end of stream encountered, check whether we should rewind or stop
             if (eof)
             {
@@ -422,7 +422,7 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
                 else
                     decodeBuffer_->SetLooped(false); // Stop after the current decode buffer has been played
             }
-            
+
             decodePosition_ = currentPos;
         }
         else
@@ -434,24 +434,24 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
             decodeBuffer_ = new Sound(context_);
             decodeBuffer_->SetSize(DecodeBufferSize);
             decodeBuffer_->SetFormat(sound_->GetIntFrequency(), true, sound_->IsStereo());
-            
+
             // Clear the decode buffer, then fill with initial audio data and set it to loop
             memset(decodeBuffer_->GetStart(), 0, DecodeBufferSize);
             sound_->Decode(decoder_, decodeBuffer_->GetStart(), DecodeBufferSize);
             decodeBuffer_->SetLooped(true);
             decodePosition_ = 0;
-            
+
             // Start playing the decode buffer
             position_ = decodeBuffer_->GetStart();
             fractPosition_ = 0;
         }
     }
-    
+
     // If compressed, play the decode buffer. Otherwise play the original sound
     Sound* sound = sound_->IsCompressed() ? decodeBuffer_ : sound_;
     if (!sound)
         return;
-    
+
     // Choose the correct mixing routine
     if (!sound->IsStereo())
     {
@@ -487,7 +487,7 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
                 MixStereoToMono(sound, dest, samples, mixRate);
         }
     }
-    
+
     // Update the time position
     if (!sound_->IsCompressed())
         timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
@@ -529,18 +529,18 @@ void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int m
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -567,7 +567,7 @@ void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int m
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -589,7 +589,7 @@ void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int m
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -603,18 +603,18 @@ void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -645,7 +645,7 @@ void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -671,7 +671,7 @@ void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -684,18 +684,18 @@ void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -722,7 +722,7 @@ void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -744,7 +744,7 @@ void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -758,18 +758,18 @@ void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, i
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -802,7 +802,7 @@ void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, i
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -830,7 +830,7 @@ void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, i
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -843,18 +843,18 @@ void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -883,7 +883,7 @@ void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -907,7 +907,7 @@ void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -920,18 +920,18 @@ void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, i
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -962,7 +962,7 @@ void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, i
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -988,7 +988,7 @@ void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, i
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -1001,18 +1001,18 @@ void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, i
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -1041,7 +1041,7 @@ void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, i
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -1065,7 +1065,7 @@ void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, i
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -1078,18 +1078,18 @@ void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples,
         MixZeroVolume(sound, samples, mixRate);
         return;
     }
-    
+
     float add = frequency_ / (float)mixRate;
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     int fractPos = fractPosition_;
-    
+
     if (sound->IsSixteenBit())
     {
         short* pos = (short*)position_;
         short* end = (short*)sound->GetEnd();
         short* repeat = (short*)sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -1120,7 +1120,7 @@ void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples,
         signed char* pos = (signed char*)position_;
         signed char* end = sound->GetEnd();
         signed char* repeat = sound->GetRepeat();
-        
+
         if (sound->IsLooped())
         {
             while (--samples)
@@ -1146,7 +1146,7 @@ void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples,
             position_ = pos;
         }
     }
-    
+
     fractPosition_ = fractPos;
 }
 
@@ -1156,7 +1156,7 @@ void SoundSource::MixZeroVolume(Sound* sound, unsigned samples, int mixRate)
     int intAdd = (int)add;
     int fractAdd = (int)((add - floorf(add)) * 65536.0f);
     unsigned sampleSize = sound->GetSampleSize();
-    
+
     fractPosition_ += fractAdd;
     if (fractPosition_ > 65535)
     {
@@ -1164,7 +1164,7 @@ void SoundSource::MixZeroVolume(Sound* sound, unsigned samples, int mixRate)
         position_ += sampleSize;
     }
     position_ += intAdd * sampleSize;
-    
+
     if (position_ > sound->GetEnd())
     {
         if (sound->IsLooped())
@@ -1183,10 +1183,10 @@ void SoundSource::MixNull(float timeStep)
 {
     if (!position_ || !sound_ || !IsEnabledEffective())
         return;
-    
+
     // Advance only the time position
     timePosition_ += timeStep * frequency_ / sound_->GetFrequency();
-    
+
     if (sound_->IsLooped())
     {
         // For simulated playback, simply reset the time position to zero when the sound loops
@@ -1210,7 +1210,7 @@ void SoundSource::FreeDecoder()
         sound_->FreeDecoder(decoder_);
         decoder_ = 0;
     }
-    
+
     decodeBuffer_.Reset();
 }
 

+ 8 - 5
Engine/Audio/SoundSource3D.cpp

@@ -52,9 +52,12 @@ SoundSource3D::SoundSource3D(Context* context) :
 
 void SoundSource3D::RegisterObject(Context* context)
 {
-    context->RegisterFactory<SoundSource3D>();
-    
+    context->RegisterComponentFactory<SoundSource3D>(AUDIO_CATEGORY);
+
     COPY_BASE_ATTRIBUTES(SoundSource3D, SoundSource);
+    // Remove Attenuation and Panning as attribute as they are constantly being updated
+    REMOVE_ATTRIBUTE(SoundSource3D, "Attenuation");
+    REMOVE_ATTRIBUTE(SoundSource3D, "Panning");
     ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Near Distance", nearDistance_, DEFAULT_NEARDISTANCE, AM_DEFAULT);
     ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Far Distance", farDistance_, DEFAULT_FARDISTANCE, AM_DEFAULT);
     ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Rolloff Factor", rolloffFactor_, DEFAULT_ROLLOFF, AM_DEFAULT);
@@ -96,12 +99,12 @@ void SoundSource3D::CalculateAttenuation()
 {
     if (!audio_)
         return;
-    
+
     float interval = farDistance_ - nearDistance_;
     if (interval > 0.0f && node_)
     {
         SoundListener* listener = audio_->GetListener();
-        
+
         // Listener must either be sceneless or in the same scene, else attenuate sound to silence
         if (listener && listener->IsEnabledEffective() && (!listener->GetScene() || listener->GetScene() == GetScene()))
         {
@@ -110,7 +113,7 @@ void SoundSource3D::CalculateAttenuation()
             float distance = Clamp(relativePos.Length() - nearDistance_, 0.0f, interval);
             float attenuation = powf(1.0f - distance / interval, rolloffFactor_);
             float panning = relativePos.Normalized().x_;
-            
+
             attenuation_ = attenuation;
             panning_ = panning;
         }

+ 6 - 0
Engine/Core/Context.cpp

@@ -82,6 +82,12 @@ void Context::RegisterFactory(ObjectFactory* factory)
     factories_[factory->GetType()] = factory;
 }
 
+void Context::RegisterComponentFactory(ObjectFactory* factory, const char* category)
+{
+    RegisterFactory(factory);
+    componentCategories_[category].Push(factory->GetType());
+}
+
 void Context::RegisterSubsystem(Object* object)
 {
     if (!object)

+ 18 - 9
Engine/Core/Context.h

@@ -33,17 +33,19 @@ namespace Urho3D
 class Context : public RefCounted
 {
     friend class Object;
-    
+
 public:
     /// Construct.
     Context();
     /// Destruct.
     ~Context();
-    
+
     /// Create an object by type hash. Return pointer to it or null if no factory found.
     SharedPtr<Object> CreateObject(ShortStringHash objectType);
     /// Register a factory for an object type.
     void RegisterFactory(ObjectFactory* factory);
+    /// Register a factory for a component type.
+    void RegisterComponentFactory(ObjectFactory* factory, const char* category);
     /// Register a subsystem.
     void RegisterSubsystem(Object* subsystem);
     /// Remove a subsystem.
@@ -54,11 +56,13 @@ public:
     void RemoveAttribute(ShortStringHash objectType, const char* name);
     /// Update object attribute's default value.
     void UpdateAttributeDefaultValue(ShortStringHash objectType, const char* name, const Variant& defaultValue);
-    
+
     /// Copy base class attributes to derived class.
     void CopyBaseAttributes(ShortStringHash baseType, ShortStringHash derivedType);
     /// Template version of registering an object factory.
     template <class T> void RegisterFactory();
+    /// Template version of registering a component factory.
+    template <class T> void RegisterComponentFactory(const char* category);
     /// Template version of removing a subsystem.
     template <class T> void RemoveSubsystem();
     /// Template version of registering an object attribute.
@@ -69,13 +73,15 @@ public:
     template <class T, class U> void CopyBaseAttributes();
     /// Template version of updating an object attribute's default value.
     template <class T> void UpdateAttributeDefaultValue(const char* name, const Variant& defaultValue);
-    
+
     /// Return subsystem by type.
     Object* GetSubsystem(ShortStringHash type) const;
     /// Return all subsystems.
     const HashMap<ShortStringHash, SharedPtr<Object> >& GetSubsystems() const { return subsystems_; }
     /// Return all object factories.
     const HashMap<ShortStringHash, SharedPtr<ObjectFactory> >& GetObjectFactories() const { return factories_; }
+    /// Return all component categories.
+    const HashMap<String, Vector<ShortStringHash> >& GetComponentCategories() const { return componentCategories_; }
     /// Return active event sender. Null outside event handling.
     Object* GetEventSender() const;
     /// Return active event handler. Set by Object. Null outside event handling.
@@ -88,21 +94,21 @@ public:
     template <class T> T* GetSubsystem() const;
     /// Template version of returning a specific attribute description.
     template <class T> AttributeInfo* GetAttribute(const char* name);
-    
+
     /// Return attribute descriptions for an object type, or null if none defined.
     const Vector<AttributeInfo>* GetAttributes(ShortStringHash type) const
     {
         HashMap<ShortStringHash, Vector<AttributeInfo> >::ConstIterator i = attributes_.Find(type);
         return i != attributes_.End() ? &i->second_ : 0;
     }
-    
+
     /// Return network replication attribute descriptions for an object type, or null if none defined.
     const Vector<AttributeInfo>* GetNetworkAttributes(ShortStringHash type) const
     {
         HashMap<ShortStringHash, Vector<AttributeInfo> >::ConstIterator i = networkAttributes_.Find(type);
         return i != networkAttributes_.End() ? &i->second_ : 0;
     }
-    
+
     /// Return event receivers for a sender and event type, or null if they do not exist.
     HashSet<Object*>* GetEventReceivers(Object* sender, StringHash eventType)
     {
@@ -115,14 +121,14 @@ public:
         else
             return 0;
     }
-    
+
     /// Return event receivers for an event type, or null if they do not exist.
     HashSet<Object*>* GetEventReceivers(StringHash eventType)
     {
         HashMap<StringHash, HashSet<Object*> >::Iterator i = eventReceivers_.Find(eventType);
         return i != eventReceivers_.End() ? &i->second_ : 0;
     }
-    
+
 private:
     /// Add event receiver.
     void AddEventReceiver(Object* receiver, StringHash eventType);
@@ -157,9 +163,12 @@ private:
     PODVector<Object*> eventSenders_;
     /// Active event handler. Not stored in a stack for performance reasons; is needed only in esoteric cases.
     EventHandler* eventHandler_;
+    /// Component categories.
+    HashMap<String, Vector<ShortStringHash> > componentCategories_;
 };
 
 template <class T> void Context::RegisterFactory() { RegisterFactory(new ObjectFactoryImpl<T>(this)); }
+template <class T> void Context::RegisterComponentFactory(const char* category) { RegisterComponentFactory(new ObjectFactoryImpl<T>(this), category); }
 template <class T> void Context::RemoveSubsystem() { RemoveSubsystem(T::GetTypeStatic()); }
 template <class T> void Context::RegisterAttribute(const AttributeInfo& attr) { RegisterAttribute(T::GetTypeStatic(), attr); }
 template <class T> void Context::RemoveAttribute(const char* name) { RemoveAttribute(T::GetTypeStatic(), name); }

+ 1 - 0
Engine/Engine/APITemplates.h

@@ -399,6 +399,7 @@ template <class T> void RegisterComponent(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "bool get_enabled() const", asMETHODPR(T, IsEnabled, () const, bool), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_enabledEffective() const", asMETHODPR(T, IsEnabledEffective, () const, bool), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_id()", asMETHODPR(T, GetID, () const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const String& get_category() const", asMETHOD(T, GetCategory), asCALL_THISCALL);
     if (nodeRegistered)
         engine->RegisterObjectMethod(className, "Node@+ get_node() const", asMETHODPR(T, GetNode, () const, Node*), asCALL_THISCALL);
     if (debugRendererRegistered)

+ 32 - 18
Engine/Engine/SceneAPI.cpp

@@ -39,7 +39,7 @@ static void RegisterSerializable(asIScriptEngine* engine)
     engine->RegisterGlobalProperty("const uint AM_NOEDIT", (void*)&AM_NOEDIT);
     engine->RegisterGlobalProperty("const uint AM_NODEID", (void*)&AM_NODEID);
     engine->RegisterGlobalProperty("const uint AM_COMPONENTID", (void*)&AM_COMPONENTID);
-    
+
     RegisterSerializable<Serializable>(engine, "Serializable");
 }
 
@@ -56,7 +56,7 @@ static void RegisterNode(asIScriptEngine* engine)
     engine->RegisterEnum("CreateMode");
     engine->RegisterEnumValue("CreateMode", "REPLICATED", REPLICATED);
     engine->RegisterEnumValue("CreateMode", "LOCAL", LOCAL);
-    
+
     // Register Component first. At this point Node is not yet registered, so can not register GetNode for Component
     RegisterComponent<Component>(engine, "Component", false, false);
     RegisterNode<Node>(engine, "Node");
@@ -68,10 +68,10 @@ static void RegisterNode(asIScriptEngine* engine)
     RegisterObjectConstructor<Node>(engine, "Node");
     RegisterNamedObjectConstructor<Node>(engine, "Node");
     engine->RegisterGlobalFunction("Node@+ get_node()", asFUNCTION(GetScriptContextNode), asCALL_CDECL);
-    
+
     // Now GetNode can be registered
     engine->RegisterObjectMethod("Component", "Node@+ get_node() const", asMETHOD(Component, GetNode), asCALL_THISCALL);
-    
+
     // Register Variant GetPtr() for Node & Component
     engine->RegisterObjectMethod("Variant", "Node@+ GetNode() const", asFUNCTION(GetVariantPtr<Node>), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "Component@+ GetComponent() const", asFUNCTION(GetVariantPtr<Component>), asCALL_CDECL_OBJLAST);
@@ -122,20 +122,33 @@ static CScriptArray* SceneGetRequiredPackageFiles(Scene* ptr)
     return VectorToHandleArray<PackageFile>(ptr->GetRequiredPackageFiles(), "Array<PackageFile@>");
 }
 
-static CScriptArray* GetAvailableComponents(Scene* ptr)
+static CScriptArray* GetComponentCategories()
+{
+    Vector<String> categories = GetScriptContext()->GetComponentCategories().Keys();
+    Sort(categories.Begin(), categories.End());
+    return VectorToArray<String>(categories, "Array<String>");
+}
+
+static CScriptArray* GetComponentsByCategory(const String& category)
 {
-    const HashMap<ShortStringHash, SharedPtr<ObjectFactory> >& factories = GetScriptContext()->GetObjectFactories();
+    const HashMap<String, Vector<ShortStringHash> >& categories = GetScriptContext()->GetComponentCategories();
     Vector<String> components;
-    
-    // Simply try to create each of the objects, and check which derive from Component.
-    // This assumes that creating any of them does not have harmful side-effects
-    for (HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories.Begin(); i != factories.End(); ++i)
+
+    HashMap<String, Vector<ShortStringHash> >::ConstIterator i = categories.Find(category);
+    if (i != categories.End())
     {
-        SharedPtr<Object> object = i->second_->CreateObject();
-        if (dynamic_cast<Component*>(object.Get()))
-            components.Push(object->GetTypeName());
+        const HashMap<ShortStringHash, SharedPtr<ObjectFactory> >& factories = GetScriptContext()->GetObjectFactories();
+        const Vector<ShortStringHash>& factoryHashes = i->second_;
+        components.Reserve(factoryHashes.Size());
+
+        for (unsigned j = 0; j < factoryHashes.Size(); ++j)
+        {
+            HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator k = factories.Find(factoryHashes[j]);
+            if (k != factories.End())
+                components.Push(k->second_->GetTypeName());
+        }
     }
-    
+
     Sort(components.Begin(), components.End());
     return VectorToArray<String>(components, "Array<String>");
 }
@@ -161,7 +174,7 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterGlobalProperty("const uint LAST_REPLICATED_ID", (void*)&LAST_REPLICATED_ID);
     engine->RegisterGlobalProperty("const uint FIRST_LOCAL_ID", (void*)&FIRST_LOCAL_ID);
     engine->RegisterGlobalProperty("const uint LAST_LOCAL_ID", (void*)&LAST_LOCAL_ID);
-    
+
     RegisterNode<Scene>(engine, "Scene");
     RegisterObjectConstructor<Scene>(engine, "Scene");
     RegisterNamedObjectConstructor<Scene>(engine, "Scene");
@@ -201,11 +214,12 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "Array<PackageFile@>@ get_requiredPackageFiles() const", asFUNCTION(SceneGetRequiredPackageFiles), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Node", "Scene@+ get_scene() const", asMETHOD(Node, GetScene), asCALL_THISCALL);
     engine->RegisterGlobalFunction("Scene@+ get_scene()", asFUNCTION(GetScriptContextScene), asCALL_CDECL);
-    
+
     // Register Variant GetPtr() for Scene
     engine->RegisterObjectMethod("Variant", "Scene@+ GetScene() const", asFUNCTION(GetVariantPtr<Scene>), asCALL_CDECL_OBJLAST);
-    
-    engine->RegisterGlobalFunction("Array<String>@ GetAvailableComponents()", asFUNCTION(GetAvailableComponents), asCALL_CDECL);
+
+    engine->RegisterGlobalFunction("Array<String>@ GetComponentCategories()", asFUNCTION(GetComponentCategories), asCALL_CDECL);
+    engine->RegisterGlobalFunction("Array<String>@ GetComponentsByCategory(const String&in)", asFUNCTION(GetComponentsByCategory), asCALL_CDECL);
 }
 
 void RegisterSceneAPI(asIScriptEngine* engine)

+ 1 - 1
Engine/Graphics/AnimatedModel.cpp

@@ -78,7 +78,7 @@ AnimatedModel::~AnimatedModel()
 
 void AnimatedModel::RegisterObject(Context* context)
 {
-    context->RegisterFactory<AnimatedModel>();
+    context->RegisterComponentFactory<AnimatedModel>(ANIMATION_CATEGORY);
 
     ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_RESOURCEREF, "Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);

+ 2 - 0
Engine/Graphics/Animation.cpp

@@ -36,6 +36,8 @@
 namespace Urho3D
 {
 
+const char* ANIMATION_CATEGORY = "Animation";
+
 inline bool CompareTriggers(AnimationTriggerPoint& lhs, AnimationTriggerPoint& rhs)
 {
     return lhs.time_ < rhs.time_;

+ 2 - 0
Engine/Graphics/Animation.h

@@ -30,6 +30,8 @@
 namespace Urho3D
 {
 
+extern const char* ANIMATION_CATEGORY;
+
 /// Skeletal animation keyframe.
 struct AnimationKeyFrame
 {

+ 1 - 1
Engine/Graphics/AnimationController.cpp

@@ -59,7 +59,7 @@ AnimationController::~AnimationController()
 
 void AnimationController::RegisterObject(Context* context)
 {
-    context->RegisterFactory<AnimationController>();
+    context->RegisterComponentFactory<AnimationController>(ANIMATION_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(AnimationController, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(AnimationController, VAR_VARIANTVECTOR, "Animations", GetAnimationsAttr, SetAnimationsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);

+ 3 - 1
Engine/Graphics/BillboardSet.cpp

@@ -41,6 +41,8 @@
 namespace Urho3D
 {
 
+extern const char* EFFECT_CATEGORY;
+
 static const float INV_SQRT_TWO = 1.0f / sqrtf(2.0f);
 
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
@@ -80,7 +82,7 @@ BillboardSet::~BillboardSet()
 
 void BillboardSet::RegisterObject(Context* context)
 {
-    context->RegisterFactory<BillboardSet>();
+    context->RegisterComponentFactory<BillboardSet>(EFFECT_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);

+ 31 - 29
Engine/Graphics/Camera.cpp

@@ -31,6 +31,8 @@
 namespace Urho3D
 {
 
+extern const char* SCENE_CATEGORY;
+
 static const float DEFAULT_NEARCLIP = 0.1f;
 static const float DEFAULT_FARCLIP = 1000.0f;
 static const float DEFAULT_FOV = 45.0f;
@@ -81,8 +83,8 @@ Camera::~Camera()
 
 void Camera::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Camera>();
-    
+    context->RegisterComponentFactory<Camera>(SCENE_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(Camera, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Camera, VAR_FLOAT, "Near Clip", GetNearClip, SetNearClip, float, DEFAULT_NEARCLIP, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Camera, VAR_FLOAT, "Far Clip", GetFarClip, SetFarClip, float, DEFAULT_FARCLIP, AM_DEFAULT);
@@ -222,54 +224,54 @@ float Camera::GetNearClip() const
 Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const
 {
     Frustum ret;
-    
+
     const Matrix3x4& worldTransform = node_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;
     nearClip = Max(nearClip, GetNearClip());
     farClip = Min(farClip, farClip_);
     if (farClip < nearClip)
         farClip = nearClip;
-    
+
     if (!orthographic_)
         ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, worldTransform);
     else
         ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, worldTransform);
-    
+
     return ret;
 }
 
 Frustum Camera::GetViewSpaceFrustum() const
 {
     Frustum ret;
-    
+
     if (!orthographic_)
         ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), farClip_);
     else
         ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), farClip_);
-    
+
     return ret;
 }
 
 Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const
 {
     Frustum ret;
-    
+
     nearClip = Max(nearClip, GetNearClip());
     farClip = Min(farClip, farClip_);
     if (farClip < nearClip)
         farClip = nearClip;
-    
+
     if (!orthographic_)
         ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip);
     else
         ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip);
-    
+
     return ret;
 }
 
 Ray Camera::GetScreenRay(float x, float y) const
 {
     Ray ret;
-    
+
     // If projection is invalid, just return a ray pointing forward
     if (!IsProjectionValid())
     {
@@ -277,15 +279,15 @@ Ray Camera::GetScreenRay(float x, float y) const
         ret.direction_ = GetForwardVector();
         return ret;
     }
-    
+
     Matrix4 viewProjInverse = (GetProjection(false) * GetInverseWorldTransform()).Inverse();
-    
+
     // The parameters range from 0.0 to 1.0. Expand to normalized device coordinates (-1.0 to 1.0) & flip Y axis
     x = 2.0f * x - 1.0f;
     y = 1.0f - 2.0f * y;
     Vector3 near(x, y, 0.0f);
     Vector3 far(x, y, 1.0f);
-    
+
     ret.origin_ = viewProjInverse * near;
     ret.direction_ = ((viewProjInverse * far) - ret.origin_).Normalized();
     return ret;
@@ -295,7 +297,7 @@ Vector2 Camera::WorldToScreenPoint(const Vector3& worldPos) const
 {
     Vector3 eyeSpacePos = GetInverseWorldTransform() * worldPos;
     Vector2 ret;
-    
+
     if(eyeSpacePos.z_ > 0.0f)
     {
         Vector3 screenSpacePos = GetProjection(false) * eyeSpacePos;
@@ -307,7 +309,7 @@ Vector2 Camera::WorldToScreenPoint(const Vector3& worldPos) const
         ret.x_ = (-eyeSpacePos.x_ > 0.0f) ? -1.0f : 1.0f;
         ret.y_ = (-eyeSpacePos.y_ > 0.0f) ? -1.0f : 1.0f;
     }
-    
+
     ret.x_ = (ret.x_ / 2.0f) + 0.5f;
     ret.y_ = 1.0f - ((ret.y_ / 2.0f) + 0.5f);
     return ret;
@@ -328,10 +330,10 @@ const Frustum& Camera::GetFrustum() const
             frustum_.Define(fov_, aspectRatio_, zoom_, GetNearClip(), farClip_, worldTransform);
         else
             frustum_.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), farClip_, worldTransform);
-        
+
         frustumDirty_ = false;
     }
-    
+
     return frustum_;
 }
 
@@ -342,21 +344,21 @@ const Matrix4& Camera::GetProjection() const
         projection_ = GetProjection(true);
         projectionDirty_ = false;
     }
-    
+
     return projection_;
 }
 
 Matrix4 Camera::GetProjection(bool apiSpecific) const
 {
     Matrix4 ret(Matrix4::ZERO);
-    
+
     if (!orthographic_)
     {
         float nearClip = GetNearClip();
         float h = (1.0f / tanf(fov_ * M_DEGTORAD * 0.5f)) * zoom_;
         float w = h / aspectRatio_;
         float q, r;
-        
+
         if (apiSpecific)
         {
             #ifdef USE_OPENGL
@@ -372,7 +374,7 @@ Matrix4 Camera::GetProjection(bool apiSpecific) const
             q = farClip_ / (farClip_ - nearClip);
             r = -q * nearClip;
         }
-        
+
         ret.m00_ = w;
         ret.m02_ = projectionOffset_.x_ * 2.0f;
         ret.m11_ = h;
@@ -387,7 +389,7 @@ Matrix4 Camera::GetProjection(bool apiSpecific) const
         float h = (1.0f / (orthoSize_ * 0.5f)) * zoom_;
         float w = h / aspectRatio_;
         float q, r;
-        
+
         if (apiSpecific)
         {
             #ifdef USE_OPENGL
@@ -403,7 +405,7 @@ Matrix4 Camera::GetProjection(bool apiSpecific) const
             q = 1.0f / farClip_;
             r = 0.0f;
         }
-        
+
         ret.m00_ = w;
         ret.m03_ = projectionOffset_.x_ * 2.0f;
         ret.m11_ = h;
@@ -412,10 +414,10 @@ Matrix4 Camera::GetProjection(bool apiSpecific) const
         ret.m23_ = r;
         ret.m33_ = 1.0f;
     }
-    
+
     if (flipVertical_)
         ret = flipMatrix * ret;
-    
+
     return ret;
 }
 
@@ -423,7 +425,7 @@ void Camera::GetFrustumSize(Vector3& near, Vector3& far) const
 {
     near.z_ = GetNearClip();
     far.z_ = farClip_;
-    
+
     if (!orthographic_)
     {
         float halfViewSize = tanf(fov_ * M_DEGTORAD * 0.5f) / zoom_;
@@ -438,7 +440,7 @@ void Camera::GetFrustumSize(Vector3& near, Vector3& far) const
         near.y_ = far.y_ = halfViewSize;
         near.x_ = far.x_ = near.y_ * aspectRatio_;
     }
-    
+
     if (flipVertical_)
     {
         near.y_ = -near.y_;
@@ -516,7 +518,7 @@ const Matrix3x4& Camera::GetInverseWorldTransform() const
         inverseWorld_ = worldTransform.Inverse();
         inverseWorldDirty_ = false;
     }
-    
+
     return inverseWorld_;
 }
 

+ 3 - 1
Engine/Graphics/CustomGeometry.cpp

@@ -39,6 +39,8 @@
 namespace Urho3D
 {
 
+extern const char* STATIC_CATEGORY;
+
 OBJECTTYPESTATIC(CustomGeometry);
 
 CustomGeometry::CustomGeometry(Context* context) :
@@ -57,7 +59,7 @@ CustomGeometry::~CustomGeometry()
 
 void CustomGeometry::RegisterObject(Context* context)
 {
-    context->RegisterFactory<CustomGeometry>();
+    context->RegisterComponentFactory<CustomGeometry>(STATIC_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(CustomGeometry, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ATTRIBUTE(CustomGeometry, VAR_BOOL, "Is Occluder", occluder_, false, AM_DEFAULT);

+ 48 - 46
Engine/Graphics/DebugRenderer.cpp

@@ -41,6 +41,8 @@
 namespace Urho3D
 {
 
+extern const char* SCENE_CATEGORY;
+
 // Cap the amount of lines to prevent crash when eg. debug rendering large heightfields
 static const unsigned MAX_LINES = 1000000;
 
@@ -50,7 +52,7 @@ DebugRenderer::DebugRenderer(Context* context) :
     Component(context)
 {
     vertexBuffer_ = new VertexBuffer(context_);
-    
+
     SubscribeToEvent(E_ENDFRAME, HANDLER(DebugRenderer, HandleEndFrame));
 }
 
@@ -60,14 +62,14 @@ DebugRenderer::~DebugRenderer()
 
 void DebugRenderer::RegisterObject(Context* context)
 {
-    context->RegisterFactory<DebugRenderer>();
+    context->RegisterComponentFactory<DebugRenderer>(SCENE_CATEGORY);
 }
 
 void DebugRenderer::SetView(Camera* camera)
 {
     if (!camera)
         return;
-    
+
     view_ = camera->GetInverseWorldTransform();
     projection_ = camera->GetProjection();
     frustum_ = camera->GetFrustum();
@@ -82,7 +84,7 @@ void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, unsigned c
 {
     if (lines_.Size() + noDepthLines_.Size() >= MAX_LINES)
         return;
-    
+
     if (depthTest)
         lines_.Push(DebugLine(start, end, color));
     else
@@ -93,10 +95,10 @@ void DebugRenderer::AddNode(Node* node, float scale, bool depthTest)
 {
     if (!node)
         return;
-    
+
     Vector3 start = node->GetWorldPosition();
     Quaternion rotation = node->GetWorldRotation();
-    
+
     AddLine(start, start + rotation * (scale * Vector3::RIGHT), Color::RED.ToUInt(), depthTest);
     AddLine(start, start + rotation * (scale * Vector3::UP), Color::GREEN.ToUInt(), depthTest);
     AddLine(start, start + rotation * (scale * Vector3::FORWARD), Color::BLUE.ToUInt(), depthTest);
@@ -106,16 +108,16 @@ void DebugRenderer::AddBoundingBox(const BoundingBox& box, const Color& color, b
 {
     const Vector3& min = box.min_;
     const Vector3& max = box.max_;
-    
+
     Vector3 v1(max.x_, min.y_, min.z_);
     Vector3 v2(max.x_, max.y_, min.z_);
     Vector3 v3(min.x_, max.y_, min.z_);
     Vector3 v4(min.x_, min.y_, max.z_);
     Vector3 v5(max.x_, min.y_, max.z_);
     Vector3 v6(min.x_, max.y_, max.z_);
-    
+
     unsigned uintColor = color.ToUInt();
-    
+
     AddLine(min, v1, uintColor, depthTest);
     AddLine(v1, v2, uintColor, depthTest);
     AddLine(v2, v3, uintColor, depthTest);
@@ -134,7 +136,7 @@ void DebugRenderer::AddBoundingBox(const BoundingBox& box, const Matrix3x4& tran
 {
     const Vector3& min = box.min_;
     const Vector3& max = box.max_;
-    
+
     Vector3 v0(transform * min);
     Vector3 v1(transform * Vector3(max.x_, min.y_, min.z_));
     Vector3 v2(transform * Vector3(max.x_, max.y_, min.z_));
@@ -143,9 +145,9 @@ void DebugRenderer::AddBoundingBox(const BoundingBox& box, const Matrix3x4& tran
     Vector3 v5(transform * Vector3(max.x_, min.y_, max.z_));
     Vector3 v6(transform * Vector3(min.x_, max.y_, max.z_));
     Vector3 v7(transform * max);
-    
+
     unsigned uintColor = color.ToUInt();
-    
+
     AddLine(v0, v1, uintColor, depthTest);
     AddLine(v1, v2, uintColor, depthTest);
     AddLine(v2, v3, uintColor, depthTest);
@@ -165,7 +167,7 @@ void DebugRenderer::AddFrustum(const Frustum& frustum, const Color& color, bool
 {
     const Vector3* vertices = frustum.vertices_;
     unsigned uintColor = color.ToUInt();
-    
+
     AddLine(vertices[0], vertices[1], uintColor, depthTest);
     AddLine(vertices[1], vertices[2], uintColor, depthTest);
     AddLine(vertices[2], vertices[3], uintColor, depthTest);
@@ -183,7 +185,7 @@ void DebugRenderer::AddFrustum(const Frustum& frustum, const Color& color, bool
 void DebugRenderer::AddPolyhedron(const Polyhedron& poly, const Color& color, bool depthTest)
 {
     unsigned uintColor = color.ToUInt();
-    
+
     for (unsigned i = 0; i < poly.faces_.Size(); ++i)
     {
         const PODVector<Vector3>& face = poly.faces_[i];
@@ -200,7 +202,7 @@ void DebugRenderer::AddSphere(const Sphere& sphere, const Color& color, bool dep
     const Vector3& center = sphere.center_;
     float radius = sphere.radius_;
     unsigned uintColor = color.ToUInt();
-    
+
     for (unsigned i = 0; i < 360; i += 45)
     {
         unsigned j = i + 45;
@@ -209,7 +211,7 @@ void DebugRenderer::AddSphere(const Sphere& sphere, const Color& color, bool dep
         float c = radius * sinf(j * M_DEGTORAD);
         float d = radius * cosf(j * M_DEGTORAD);
         Vector3 start, end;
-        
+
         start = center + Vector3(a, b, 0.0f);
         end = center + Vector3(c, d, 0.0f);
         AddLine(start, end, uintColor, depthTest);
@@ -227,31 +229,31 @@ void DebugRenderer::AddSkeleton(const Skeleton& skeleton, const Color& color, bo
     const Vector<Bone>& bones = skeleton.GetBones();
     if (!bones.Size())
         return;
-    
+
     unsigned uintColor = color.ToUInt();
-    
+
     for (unsigned i = 0; i < bones.Size(); ++i)
     {
         // Skip if bone contains no skinned geometry
         if (bones[i].radius_ < M_EPSILON && bones[i].boundingBox_.Size().LengthSquared() < M_EPSILON)
             continue;
-        
+
         Node* boneNode = bones[i].node_;
         if (!boneNode)
             continue;
-        
+
         Vector3 start = boneNode->GetWorldPosition();
         Vector3 end;
-        
+
         unsigned j = bones[i].parentIndex_;
         Node* parentNode = boneNode->GetParent();
-        
+
         // If bone has a parent defined, and it also skins geometry, draw a line to it. Else draw the bone as a point
         if (parentNode && (bones[j].radius_ >= M_EPSILON || bones[j].boundingBox_.Size().LengthSquared() >= M_EPSILON))
             end = parentNode->GetWorldPosition();
         else
             end = start;
-        
+
         AddLine(start, end, uintColor, depthTest);
     }
 }
@@ -261,23 +263,23 @@ void DebugRenderer::AddTriangleMesh(const void* vertexData, unsigned vertexSize,
 {
     unsigned uintColor = color.ToUInt();
     const unsigned char* srcData = (const unsigned char*)vertexData;
-    
+
     // 16-bit indices
     if (indexSize == sizeof(unsigned short))
     {
         const unsigned short* indices = ((const unsigned short*)indexData) + indexStart;
         const unsigned short* indicesEnd = indices + indexCount;
-        
+
         while (indices < indicesEnd)
         {
             Vector3 v0 = transform * *((const Vector3*)(&srcData[indices[0] * vertexSize]));
             Vector3 v1 = transform * *((const Vector3*)(&srcData[indices[1] * vertexSize]));
             Vector3 v2 = transform * *((const Vector3*)(&srcData[indices[2] * vertexSize]));
-            
+
             AddLine(v0, v1, uintColor, depthTest);
             AddLine(v1, v2, uintColor, depthTest);
             AddLine(v2, v0, uintColor, depthTest);
-            
+
             indices += 3;
         }
     }
@@ -285,17 +287,17 @@ void DebugRenderer::AddTriangleMesh(const void* vertexData, unsigned vertexSize,
     {
         const unsigned* indices = ((const unsigned*)indexData) + indexStart;
         const unsigned* indicesEnd = indices + indexCount;
-        
+
         while (indices < indicesEnd)
         {
             Vector3 v0 = transform * *((const Vector3*)(&srcData[indices[0] * vertexSize]));
             Vector3 v1 = transform * *((const Vector3*)(&srcData[indices[1] * vertexSize]));
             Vector3 v2 = transform * *((const Vector3*)(&srcData[indices[2] * vertexSize]));
-            
+
             AddLine(v0, v1, uintColor, depthTest);
             AddLine(v1, v2, uintColor, depthTest);
             AddLine(v2, v0, uintColor, depthTest);
-            
+
             indices += 3;
         }
     }
@@ -305,48 +307,48 @@ void DebugRenderer::Render()
 {
     if (lines_.Empty() && noDepthLines_.Empty())
         return;
-    
+
     Graphics* graphics = GetSubsystem<Graphics>();
     Renderer* renderer = GetSubsystem<Renderer>();
-    
+
     if (!graphics || graphics->IsDeviceLost())
         return;
-    
+
     PROFILE(RenderDebugGeometry);
-    
+
     unsigned numVertices = (lines_.Size() + noDepthLines_.Size()) * 2;
     // Resize the vertex buffer if too small or much too large
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR, true);
-    
+
     float* dest = (float*)vertexBuffer_->Lock(0, numVertices, true);
     if (!dest)
         return;
-    
+
     for (unsigned i = 0; i < lines_.Size(); ++i)
     {
         const DebugLine& line = lines_[i];
-        
+
         *dest++ = line.start_.x_; *dest++ = line.start_.y_; *dest++ = line.start_.z_;
         *((unsigned*)dest) = line.color_; dest++;
-        
+
         *dest++ = line.end_.x_; *dest++ = line.end_.y_; *dest++ = line.end_.z_;
         *((unsigned*)dest) = line.color_; dest++;
     }
-    
+
     for (unsigned i = 0; i < noDepthLines_.Size(); ++i)
     {
         const DebugLine& line = noDepthLines_[i];
-        
+
         *dest++ = line.start_.x_; *dest++ = line.start_.y_; *dest++ = line.start_.z_;
         *((unsigned*)dest) = line.color_; dest++;
-        
+
         *dest++ = line.end_.x_; *dest++ = line.end_.y_; *dest++ = line.end_.z_;
         *((unsigned*)dest) = line.color_; dest++;
     }
-    
+
     vertexBuffer_->Unlock();
-    
+
     graphics->SetBlendMode(BLEND_REPLACE);
     graphics->SetColorWrite(true);
     graphics->SetCullMode(CULL_NONE);
@@ -358,7 +360,7 @@ void DebugRenderer::Render()
     graphics->SetShaderParameter(VSP_VIEWPROJ, projection_ * view_);
     graphics->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
     graphics->SetVertexBuffer(vertexBuffer_);
-    
+
     if (lines_.Size())
     {
         graphics->SetDepthTest(CMP_LESSEQUAL);
@@ -381,10 +383,10 @@ void DebugRenderer::HandleEndFrame(StringHash eventType, VariantMap& eventData)
     // When the amount of debug geometry is reduced, release memory
     unsigned linesSize = lines_.Size();
     unsigned noDepthLinesSize = noDepthLines_.Size();
-    
+
     lines_.Clear();
     noDepthLines_.Clear();
-    
+
     if (lines_.Capacity() > linesSize * 2)
         lines_.Reserve(linesSize);
     if (noDepthLines_.Capacity() > noDepthLinesSize * 2)

+ 1 - 1
Engine/Graphics/DecalSet.cpp

@@ -178,7 +178,7 @@ DecalSet::~DecalSet()
 
 void DecalSet::RegisterObject(Context* context)
 {
-    context->RegisterFactory<DecalSet>();
+    context->RegisterComponentFactory<DecalSet>(EFFECT_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(DecalSet, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(DecalSet, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);

+ 28 - 26
Engine/Graphics/Light.cpp

@@ -37,6 +37,8 @@
 namespace Urho3D
 {
 
+extern const char* SCENE_CATEGORY;
+
 static const LightType DEFAULT_LIGHTTYPE = LIGHT_POINT;
 static const float DEFAULT_RANGE = 10.0f;
 static const float DEFAULT_FOV = 30.0f;
@@ -110,11 +112,11 @@ Light::~Light()
 
 void Light::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Light>();
-    
+    context->RegisterComponentFactory<Light>(SCENE_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(Light, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE(Light, "Light Type", GetLightType, SetLightType, LightType, typeNames, DEFAULT_LIGHTTYPE, AM_DEFAULT);
-    REF_ACCESSOR_ATTRIBUTE(Light, VAR_COLOR, "Color", GetColor, SetColor, Color, Color(), AM_DEFAULT);
+    REF_ACCESSOR_ATTRIBUTE(Light, VAR_COLOR, "Color", GetColor, SetColor, Color, Color::WHITE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Specular Intensity", GetSpecularIntensity, SetSpecularIntensity, float, DEFAULT_SPECULARINTENSITY, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Range", GetRange, SetRange, float, DEFAULT_RANGE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Spot FOV", GetFov, SetFov, float, DEFAULT_FOV, AM_DEFAULT);
@@ -148,7 +150,7 @@ void Light::RegisterObject(Context* context)
 void Light::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
     Component::OnSetAttribute(attr, src);
-    
+
     // Validate the bias, cascade & focus parameters
     if (attr.offset_ >= offsetof(Light, shadowBias_) && attr.offset_ < (offsetof(Light, shadowBias_) + sizeof(BiasParameters)))
         shadowBias_.Validate();
@@ -163,7 +165,7 @@ void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResul
     // Do not record a raycast result for a directional light, as it would block all other results
     if (lightType_ == LIGHT_DIRECTIONAL)
         return;
-    
+
     float distance;
     switch (query.level_)
     {
@@ -171,7 +173,7 @@ void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResul
     case RAY_AABB:
         Drawable::ProcessRayQuery(query, results);
         return;
-        
+
     case RAY_OBB:
         {
             Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
@@ -181,7 +183,7 @@ void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResul
                 return;
         }
         break;
-        
+
     case RAY_TRIANGLE:
         if (lightType_ == LIGHT_SPOT)
         {
@@ -215,7 +217,7 @@ void Light::UpdateBatches(const FrameInfo& frame)
         // Directional light affects the whole scene, so it is always "closest"
         distance_ = 0.0f;
         break;
-        
+
     default:
         distance_ = frame.camera_->GetDistance(node_->GetWorldPosition());
         break;
@@ -241,11 +243,11 @@ void Light::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
                     }
             }
             break;
-            
+
         case LIGHT_SPOT:
             debug->AddFrustum(GetFrustum(), color_, depthTest);
             break;
-            
+
         case LIGHT_POINT:
             debug->AddSphere(Sphere(node_->GetWorldPosition(), range_), color_, depthTest);
             break;
@@ -377,35 +379,35 @@ Matrix3x4 Light::GetDirLightTransform(Camera* camera, bool getNearQuad)
 {
     if (!camera)
         return Matrix3x4::IDENTITY;
-    
+
     Vector3 nearVector, farVector;
     camera->GetFrustumSize(nearVector, farVector);
     float nearClip = camera->GetNearClip();
     float farClip = camera->GetFarClip();
-    
+
     float distance = getNearQuad ? nearClip : farClip;
     if (!camera->IsOrthographic())
         farVector *= (distance / farClip);
     else
         farVector.z_ *= (distance / farClip);
-    
+
     // Set an epsilon from clip planes due to possible inaccuracy
     /// \todo Rather set an identity projection matrix
     farVector.z_ = Clamp(farVector.z_, (1.0f + M_LARGE_EPSILON) * nearClip, (1.0f - M_LARGE_EPSILON) * farClip);
-    
+
     return  Matrix3x4(Vector3(0.0f, 0.0f, farVector.z_), Quaternion::IDENTITY, Vector3(farVector.x_, farVector.y_, 1.0f));
 }
 
 const Matrix3x4& Light::GetVolumeTransform(Camera* camera)
 {
     const Matrix3x4& transform = node_->GetWorldTransform();
-    
+
     switch (lightType_)
     {
     case LIGHT_DIRECTIONAL:
         volumeTransform_ = GetDirLightTransform(camera);
         break;
-        
+
     case LIGHT_SPOT:
         {
             float yScale = tanf(fov_ * M_DEGTORAD * 0.5f) * range_;
@@ -413,12 +415,12 @@ const Matrix3x4& Light::GetVolumeTransform(Camera* camera)
             volumeTransform_ = Matrix3x4(transform.Translation(), transform.Rotation(), Vector3(xScale, yScale, range_));
         }
         break;
-        
+
     case LIGHT_POINT:
         volumeTransform_ = Matrix3x4(transform.Translation(), Quaternion::IDENTITY, range_);
         break;
     }
-    
+
     return volumeTransform_;
 }
 
@@ -452,12 +454,12 @@ void Light::OnWorldBoundingBoxUpdate()
         // Directional light always sets humongous bounding box not affected by transform
         worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
         break;
-            
+
     case LIGHT_SPOT:
         // Frustum is already transformed into world space
         worldBoundingBox_.Define(GetFrustum());
         break;
-        
+
     case LIGHT_POINT:
         {
             const Vector3& center = node_->GetWorldPosition();
@@ -475,7 +477,7 @@ void Light::SetIntensitySortValue(float distance)
         sortValue_ = Max(distance, M_MIN_NEARCLIP) / (color_.Intensity() + M_EPSILON);
     else
         sortValue_ = M_EPSILON / (color_.Intensity() + M_EPSILON);
-    
+
     // Additionally, give priority to vertex lights so that vertex light base passes can be determined before per pixel lights
     if (perVertex_)
         sortValue_ -= M_LARGE_VALUE;
@@ -489,26 +491,26 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
     case LIGHT_DIRECTIONAL:
         sortValue_ = 1.0f / (color_.Intensity() + M_EPSILON);
         break;
-        
+
     case LIGHT_SPOT:
         {
             Vector3 centerPos = box.Center();
             Vector3 lightPos = node_->GetWorldPosition();
             Vector3 lightDir = node_->GetWorldDirection();
             Ray lightRay(lightPos, lightDir);
-            
+
             Vector3 centerProj = lightRay.Project(centerPos);
             float centerDistance = (centerProj - lightPos).Length();
             Ray centerRay(centerProj, (centerPos - centerProj).Normalized());
             float centerAngle = centerRay.HitDistance(box) / centerDistance;
-            
+
             // Check if a corner of the bounding box is closer to the light ray than the center, use its angle in that case
             Vector3 cornerPos = centerPos + box.HalfSize() * Vector3(centerPos.x_ < centerProj.x_ ? 1.0f : -1.0f,
                 centerPos.y_ < centerProj.y_ ? 1.0f : -1.0f, centerPos.z_ < centerProj.z_ ? 1.0f : -1.0f);
             Vector3 cornerProj = lightRay.Project(cornerPos);
             float cornerDistance = (cornerProj - lightPos).Length();
             float cornerAngle = (cornerPos - cornerProj).Length() / cornerDistance;
-            
+
             float spotAngle = Min(centerAngle, cornerAngle);
             float maxAngle = tanf(fov_ * M_DEGTORAD * 0.5f);
             float spotFactor = Min(spotAngle / maxAngle, 1.0f);
@@ -517,7 +519,7 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
             sortValue_ = 1.0f / (color_.Intensity() * att + M_EPSILON);
         }
         break;
-            
+
     case LIGHT_POINT:
         {
             Vector3 centerPos = box.Center();

+ 67 - 67
Engine/Graphics/Octree.cpp

@@ -52,7 +52,7 @@ void RaycastDrawablesWork(const WorkItem* item, unsigned threadIndex)
     Drawable** end = reinterpret_cast<Drawable**>(item->end_);
     const RayOctreeQuery& query = *octree->rayQuery_;
     PODVector<RayQueryResult>& results = octree->rayQueryResults_[threadIndex];
-    
+
     while (start != end)
     {
         Drawable* drawable = *start;
@@ -66,7 +66,7 @@ void UpdateDrawablesWork(const WorkItem* item, unsigned threadIndex)
     const FrameInfo& frame = *(reinterpret_cast<FrameInfo*>(item->aux_));
     WeakPtr<Drawable>* start = reinterpret_cast<WeakPtr<Drawable>*>(item->start_);
     WeakPtr<Drawable>* end = reinterpret_cast<WeakPtr<Drawable>*>(item->end_);
-    
+
     while (start != end)
     {
         Drawable* drawable = *start;
@@ -92,7 +92,7 @@ Octant::Octant(const BoundingBox& box, unsigned level, Octant* parent, Octree* r
     index_(index)
 {
     Initialize(box);
-    
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
         children_[i] = 0;
 }
@@ -111,7 +111,7 @@ Octant::~Octant()
         drawables_.Clear();
         numDrawables_ = 0;
     }
-    
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
         DeleteChild(i);
 }
@@ -120,26 +120,26 @@ Octant* Octant::GetOrCreateChild(unsigned index)
 {
     if (children_[index])
         return children_[index];
-    
+
     Vector3 newMin = worldBoundingBox_.min_;
     Vector3 newMax = worldBoundingBox_.max_;
     Vector3 oldCenter = worldBoundingBox_.Center();
-    
+
     if (index & 1)
         newMin.x_ = oldCenter.x_;
     else
         newMax.x_ = oldCenter.x_;
-    
+
     if (index & 2)
         newMin.y_ = oldCenter.y_;
     else
         newMax.y_ = oldCenter.y_;
-    
+
     if (index & 4)
         newMin.z_ = oldCenter.z_;
     else
         newMax.z_ = oldCenter.z_;
-    
+
     children_[index] = new Octant(BoundingBox(newMin, newMax), level_ + 1, this, root_, index);
     return children_[index];
 }
@@ -154,7 +154,7 @@ void Octant::DeleteChild(unsigned index)
 void Octant::InsertDrawable(Drawable* drawable)
 {
     const BoundingBox& box = drawable->GetWorldBoundingBox();
-    
+
     // If root octant, insert all non-occludees here, so that octant occlusion does not hide the drawable.
     // Also if drawable is outside the root octant bounds, insert to root
     bool insertHere;
@@ -162,7 +162,7 @@ void Octant::InsertDrawable(Drawable* drawable)
         insertHere = !drawable->IsOccludee() || cullingBox_.IsInside(box) != INSIDE || CheckDrawableFit(box);
     else
         insertHere = CheckDrawableFit(box);
-    
+
     if (insertHere)
     {
         Octant* oldOctant = drawable->octant_;
@@ -180,7 +180,7 @@ void Octant::InsertDrawable(Drawable* drawable)
         unsigned x = boxCenter.x_ < center_.x_ ? 0 : 1;
         unsigned y = boxCenter.y_ < center_.y_ ? 0 : 2;
         unsigned z = boxCenter.z_ < center_.z_ ? 0 : 4;
-        
+
         GetOrCreateChild(x + y + z)->InsertDrawable(drawable);
     }
 }
@@ -188,7 +188,7 @@ void Octant::InsertDrawable(Drawable* drawable)
 bool Octant::CheckDrawableFit(const BoundingBox& box) const
 {
     Vector3 boxSize = box.Size();
-    
+
     // If max split level, size always OK, otherwise check that box is at least half size of octant
     if (level_ >= root_->GetNumLevels() || boxSize.x_ >= halfSize_.x_ || boxSize.y_ >= halfSize_.y_ ||
         boxSize.z_ >= halfSize_.z_)
@@ -204,7 +204,7 @@ bool Octant::CheckDrawableFit(const BoundingBox& box) const
             box.max_.z_ >= worldBoundingBox_.max_.z_ + 0.5f * halfSize_.z_)
             return true;
     }
-    
+
     // Bounding box too small, should create a child octant
     return false;
 }
@@ -212,7 +212,7 @@ bool Octant::CheckDrawableFit(const BoundingBox& box) const
 void Octant::ResetRoot()
 {
     root_ = 0;
-    
+
     // The whole octree is being destroyed, just detach the drawables
     for (PODVector<Drawable*>::Iterator i = drawables_.Begin(); i != drawables_.End(); ++i)
         (*i)->SetOctant(0);
@@ -229,7 +229,7 @@ void Octant::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     if (debug && debug->IsInside(worldBoundingBox_))
     {
         debug->AddBoundingBox(worldBoundingBox_, Color(0.25f, 0.25f, 0.25f), depthTest);
-        
+
         for (unsigned i = 0; i < NUM_OCTANTS; ++i)
         {
             if (children_[i])
@@ -259,14 +259,14 @@ void Octant::GetDrawablesInternal(OctreeQuery& query, bool inside) const
             return;
         }
     }
-    
+
     if (drawables_.Size())
     {
         Drawable** start = const_cast<Drawable**>(&drawables_[0]);
         Drawable** end = start + drawables_.Size();
         query.TestDrawables(start, end, inside);
     }
-    
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     {
         if (children_[i])
@@ -279,21 +279,21 @@ void Octant::GetDrawablesInternal(RayOctreeQuery& query) const
     float octantDist = query.ray_.HitDistance(cullingBox_);
     if (octantDist >= query.maxDistance_)
         return;
-    
+
     if (drawables_.Size())
     {
         Drawable** start = const_cast<Drawable**>(&drawables_[0]);
         Drawable** end = start + drawables_.Size();
-       
+
         while (start != end)
         {
             Drawable* drawable = *start++;
-            
+
             if ((drawable->GetDrawableFlags() & query.drawableFlags_) && (drawable->GetViewMask() & query.viewMask_))
                 drawable->ProcessRayQuery(query, query.result_);
         }
     }
-    
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     {
         if (children_[i])
@@ -306,21 +306,21 @@ void Octant::GetDrawablesOnlyInternal(RayOctreeQuery& query, PODVector<Drawable*
     float octantDist = query.ray_.HitDistance(cullingBox_);
     if (octantDist >= query.maxDistance_)
         return;
-    
+
     if (drawables_.Size())
     {
         Drawable** start = const_cast<Drawable**>(&drawables_[0]);
         Drawable** end = start + drawables_.Size();
-        
+
         while (start != end)
         {
             Drawable* drawable = *start++;
-            
+
             if ((drawable->GetDrawableFlags() & query.drawableFlags_) && (drawable->GetViewMask() & query.viewMask_))
                 drawables.Push(drawable);
         }
     }
-    
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     {
         if (children_[i])
@@ -348,11 +348,11 @@ Octree::~Octree()
 
 void Octree::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Octree>();
-    
+    context->RegisterComponentFactory<Octree>(SCENE_CATEGORY);
+
     Vector3 defaultBoundsMin = -Vector3::ONE * DEFAULT_OCTREE_SIZE;
     Vector3 defaultBoundsMax = Vector3::ONE * DEFAULT_OCTREE_SIZE;
-    
+
     ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Min", worldBoundingBox_.min_, defaultBoundsMin, AM_DEFAULT);
     ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Max", worldBoundingBox_.max_, defaultBoundsMax, AM_DEFAULT);
     ATTRIBUTE(Octree, VAR_INT, "Number of Levels", numLevels_, DEFAULT_OCTREE_LEVELS, AM_DEFAULT);
@@ -370,7 +370,7 @@ void Octree::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     if (debug)
     {
         PROFILE(OctreeDrawDebug);
-        
+
         Octant::DrawDebugGeometry(debug, depthTest);
     }
 }
@@ -378,11 +378,11 @@ void Octree::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void Octree::Resize(const BoundingBox& box, unsigned numLevels)
 {
     PROFILE(ResizeOctree);
-    
+
     // If drawables exist, they are temporarily moved to the root
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
         DeleteChild(i);
-    
+
     Initialize(box);
     numDrawables_ = drawables_.Size();
     numLevels_ = Max((int)numLevels, 1);
@@ -391,19 +391,19 @@ void Octree::Resize(const BoundingBox& box, unsigned numLevels)
 void Octree::Update(const FrameInfo& frame)
 {
     UpdateDrawables(frame);
-    
+
     // Notify drawable update being finished. Custom animation (eg. IK) can be done at this point
     Scene* scene = GetScene();
     if (scene)
     {
         using namespace SceneDrawableUpdateFinished;
-        
+
         VariantMap eventData;
         eventData[P_SCENE] = (void*)scene;
         eventData[P_TIMESTEP] = frame.timeStep_;
         scene->SendEvent(E_SCENEDRAWABLEUPDATEFINISHED, eventData);
     }
-    
+
     ReinsertDrawables(frame);
 }
 
@@ -411,7 +411,7 @@ void Octree::AddManualDrawable(Drawable* drawable)
 {
     if (!drawable || drawable->GetOctant())
         return;
-    
+
     AddDrawable(drawable);
 }
 
@@ -419,7 +419,7 @@ void Octree::RemoveManualDrawable(Drawable* drawable)
 {
     if (!drawable)
         return;
-    
+
     Octant* octant = drawable->GetOctant();
     if (octant && octant->GetRoot() == this)
         octant->RemoveDrawable(drawable);
@@ -434,11 +434,11 @@ void Octree::GetDrawables(OctreeQuery& query) const
 void Octree::Raycast(RayOctreeQuery& query) const
 {
     PROFILE(Raycast);
-    
+
     query.result_.Clear();
-    
+
     WorkQueue* queue = GetSubsystem<WorkQueue>();
-    
+
     // If no worker threads or no triangle-level testing, do not create work items
     if (!queue->GetNumThreads() || query.level_ < RAY_TRIANGLE)
         GetDrawablesInternal(query);
@@ -448,31 +448,31 @@ void Octree::Raycast(RayOctreeQuery& query) const
         rayQuery_ = &query;
         rayQueryDrawables_.Clear();
         GetDrawablesOnlyInternal(query, rayQueryDrawables_);
-        
+
         // Check that amount of drawables is large enough to justify threading
         if (rayQueryDrawables_.Size() > RAYCASTS_PER_WORK_ITEM)
         {
             for (unsigned i = 0; i < rayQueryResults_.Size(); ++i)
                 rayQueryResults_[i].Clear();
-            
+
             WorkItem item;
             item.workFunction_ = RaycastDrawablesWork;
             item.aux_ = const_cast<Octree*>(this);
-            
+
             PODVector<Drawable*>::Iterator start = rayQueryDrawables_.Begin();
             while (start != rayQueryDrawables_.End())
             {
                 PODVector<Drawable*>::Iterator end = rayQueryDrawables_.End();
                 if (end - start > RAYCASTS_PER_WORK_ITEM)
                     end = start + RAYCASTS_PER_WORK_ITEM;
-                
+
                 item.start_ = &(*start);
                 item.end_ = &(*end);
                 queue->AddWorkItem(item);
-                
+
                 start = end;
             }
-            
+
             // Merge per-thread results
             queue->Complete(M_MAX_UNSIGNED);
             for (unsigned i = 0; i < rayQueryResults_.Size(); ++i)
@@ -484,27 +484,27 @@ void Octree::Raycast(RayOctreeQuery& query) const
                 (*i)->ProcessRayQuery(query, query.result_);
         }
     }
-    
+
     Sort(query.result_.Begin(), query.result_.End(), CompareRayQueryResults);
 }
 
 void Octree::RaycastSingle(RayOctreeQuery& query) const
 {
     PROFILE(Raycast);
-    
+
     query.result_.Clear();
     rayQueryDrawables_.Clear();
     GetDrawablesOnlyInternal(query, rayQueryDrawables_);
-    
+
     // Sort by increasing hit distance to AABB
     for (PODVector<Drawable*>::Iterator i = rayQueryDrawables_.Begin(); i != rayQueryDrawables_.End(); ++i)
     {
         Drawable* drawable = *i;
         drawable->SetSortValue(query.ray_.HitDistance(drawable->GetWorldBoundingBox()));
     }
-    
+
     Sort(rayQueryDrawables_.Begin(), rayQueryDrawables_.End(), CompareDrawables);
-    
+
     // Then do the actual test according to the query, and early-out as possible
     float closestHit = M_INFINITY;
     for (PODVector<Drawable*>::Iterator i = rayQueryDrawables_.Begin(); i != rayQueryDrawables_.End(); ++i)
@@ -520,7 +520,7 @@ void Octree::RaycastSingle(RayOctreeQuery& query) const
         else
             break;
     }
-    
+
     if (query.result_.Size() > 1)
     {
         Sort(query.result_.Begin(), query.result_.End(), CompareRayQueryResults);
@@ -544,7 +544,7 @@ void Octree::QueueReinsertion(Drawable* drawable)
     }
     else
         drawableReinsertions_.Push(WeakPtr<Drawable>(drawable));
-    
+
     drawable->reinsertionQueued_ = true;
 }
 
@@ -559,31 +559,31 @@ void Octree::UpdateDrawables(const FrameInfo& frame)
     // Let drawables update themselves before reinsertion. This can be used for animation
     if (drawableUpdates_.Empty())
         return;
-    
+
     PROFILE(UpdateDrawables);
-    
+
     Scene* scene = GetScene();
     WorkQueue* queue = GetSubsystem<WorkQueue>();
     scene->BeginThreadedUpdate();
-    
+
     WorkItem item;
     item.workFunction_ = UpdateDrawablesWork;
     item.aux_ = const_cast<FrameInfo*>(&frame);
-    
+
     Vector<WeakPtr<Drawable> >::Iterator start = drawableUpdates_.Begin();
     while (start != drawableUpdates_.End())
     {
         Vector<WeakPtr<Drawable> >::Iterator end = drawableUpdates_.End();
         if (end - start > DRAWABLES_PER_WORK_ITEM)
             end = start + DRAWABLES_PER_WORK_ITEM;
-        
+
         item.start_ = &(*start);
         item.end_ = &(*end);
         queue->AddWorkItem(item);
-        
+
         start = end;
     }
-    
+
     queue->Complete(M_MAX_UNSIGNED);
     scene->EndThreadedUpdate();
     drawableUpdates_.Clear();
@@ -595,28 +595,28 @@ void Octree::ReinsertDrawables(const FrameInfo& frame)
     // the proper octant yet
     if (drawableReinsertions_.Empty())
         return;
-    
+
     PROFILE(ReinsertToOctree);
-    
+
     for (Vector<WeakPtr<Drawable> >::Iterator i = drawableReinsertions_.Begin(); i != drawableReinsertions_.End(); ++i)
     {
         Drawable* drawable = *i;
         if (!drawable)
             continue;
-        
+
         drawable->reinsertionQueued_ = false;
         Octant* octant = drawable->GetOctant();
         const BoundingBox& box = drawable->GetWorldBoundingBox();
-        
+
         // Skip if no octant or does not belong to this octree anymore
         if (!octant || octant->GetRoot() != this)
             continue;
         // Skip if still fits the current octant
         if (drawable->IsOccludee() && octant->GetCullingBox().IsInside(box) == INSIDE && octant->CheckDrawableFit(box))
             continue;
-        
+
         InsertDrawable(drawable);
-        
+
         #ifdef _DEBUG
         // Verify that the drawable will be culled correctly
         octant = drawable->GetOctant();
@@ -625,7 +625,7 @@ void Octree::ReinsertDrawables(const FrameInfo& frame)
                 octant->GetCullingBox().ToString());
         #endif
     }
-    
+
     drawableReinsertions_.Clear();
 }
 

+ 1 - 1
Engine/Graphics/ParticleEmitter.cpp

@@ -79,7 +79,7 @@ ParticleEmitter::~ParticleEmitter()
 
 void ParticleEmitter::RegisterObject(Context* context)
 {
-    context->RegisterFactory<ParticleEmitter>();
+    context->RegisterComponentFactory<ParticleEmitter>(EFFECT_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Parameter Source", GetParameterSourceAttr, SetParameterSourceAttr, ResourceRef, ResourceRef(XMLFile::GetTypeStatic()), AM_DEFAULT);

+ 6 - 4
Engine/Graphics/Skybox.cpp

@@ -32,6 +32,8 @@
 namespace Urho3D
 {
 
+static const char* SKY_CATEGORY = "Sky";
+
 OBJECTTYPESTATIC(Skybox);
 
 Skybox::Skybox(Context* context) :
@@ -46,8 +48,8 @@ Skybox::~Skybox()
 
 void Skybox::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Skybox>();
-    
+    context->RegisterComponentFactory<Skybox>(SKY_CATEGORY);
+
     COPY_BASE_ATTRIBUTES(Skybox, StaticModel);
 }
 
@@ -59,11 +61,11 @@ void Skybox::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResu
 void Skybox::UpdateBatches(const FrameInfo& frame)
 {
     distance_ = 0.0f;
-    
+
     // Follow only the camera rotation, not position
     Matrix3x4 customView(Vector3::ZERO, frame.camera_->GetNode()->GetWorldRotation().Inverse(), Vector3::ONE);
     customWorldTransform_ = customView * node_->GetWorldTransform();
-    
+
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
         batches_[i].worldTransform_ = &customWorldTransform_;

+ 3 - 1
Engine/Graphics/StaticModel.cpp

@@ -40,6 +40,8 @@
 namespace Urho3D
 {
 
+const char* STATIC_CATEGORY = "Static";
+
 OBJECTTYPESTATIC(StaticModel);
 
 StaticModel::StaticModel(Context* context) :
@@ -55,7 +57,7 @@ StaticModel::~StaticModel()
 
 void StaticModel::RegisterObject(Context* context)
 {
-    context->RegisterFactory<StaticModel>();
+    context->RegisterComponentFactory<StaticModel>(STATIC_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(StaticModel, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(StaticModel, VAR_RESOURCEREF, "Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);

+ 92 - 90
Engine/Graphics/Terrain.cpp

@@ -44,6 +44,8 @@
 namespace Urho3D
 {
 
+const char* TERRAIN_CATEGORY = "Terrain";
+
 OBJECTTYPESTATIC(Terrain);
 
 static const Vector3 DEFAULT_SPACING(1.0f, 0.25f, 1.0f);
@@ -89,8 +91,8 @@ Terrain::~Terrain()
 
 void Terrain::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Terrain>();
-    
+    context->RegisterComponentFactory<Terrain>(TERRAIN_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(Terrain, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Terrain, VAR_RESOURCEREF, "Height Map", GetHeightMapAttr, SetHeightMapAttr, ResourceRef, ResourceRef(Image::GetTypeStatic()), AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Terrain, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
@@ -106,13 +108,13 @@ void Terrain::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(Terrain, VAR_INT, "View Mask", GetViewMask, SetViewMask, unsigned, DEFAULT_VIEWMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Terrain, VAR_INT, "Light Mask", GetLightMask, SetLightMask, unsigned, DEFAULT_LIGHTMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Terrain, VAR_INT, "Shadow Mask", GetShadowMask, SetShadowMask, unsigned, DEFAULT_SHADOWMASK, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(Terrain, VAR_INT, "Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_SHADOWMASK, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Terrain, VAR_INT, "Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_ZONEMASK, AM_DEFAULT);
 }
 
 void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
     Component::OnSetAttribute(attr, src);
-    
+
     // Change of any non-accessor attribute requires recreation of the terrain
     if (!attr.accessor_)
         recreateTerrain_ = true;
@@ -127,7 +129,7 @@ void Terrain::ApplyAttributes()
 void Terrain::OnSetEnabled()
 {
     bool enabled = IsEnabledEffective();
-    
+
     for (unsigned i = 0; i < patches_.Size(); ++i)
     {
         if (patches_[i])
@@ -140,7 +142,7 @@ void Terrain::SetSpacing(const Vector3& spacing)
     if (spacing != spacing_)
     {
         spacing_ = spacing;
-        
+
         CreateGeometry();
         MarkNetworkUpdate();
     }
@@ -150,11 +152,11 @@ void Terrain::SetPatchSize(int size)
 {
     if (size < MIN_PATCH_SIZE || size > MAX_PATCH_SIZE || !IsPowerOfTwo(size))
         return;
-    
+
     if (size != patchSize_)
     {
         patchSize_ = size;
-        
+
         CreateGeometry();
         MarkNetworkUpdate();
     }
@@ -163,7 +165,7 @@ void Terrain::SetPatchSize(int size)
 bool Terrain::SetHeightMap(Image* image)
 {
     bool success = SetHeightMapInternal(image, true);
-    
+
     MarkNetworkUpdate();
     return success;
 }
@@ -176,7 +178,7 @@ void Terrain::SetMaterial(Material* material)
         if (patches_[i])
             patches_[i]->SetMaterial(material);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -188,7 +190,7 @@ void Terrain::SetDrawDistance(float distance)
         if (patches_[i])
             patches_[i]->SetDrawDistance(distance);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -200,7 +202,7 @@ void Terrain::SetShadowDistance(float distance)
         if (patches_[i])
             patches_[i]->SetShadowDistance(distance);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -212,7 +214,7 @@ void Terrain::SetLodBias(float bias)
         if (patches_[i])
             patches_[i]->SetLodBias(bias);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -224,7 +226,7 @@ void Terrain::SetViewMask(unsigned mask)
         if (patches_[i])
             patches_[i]->SetViewMask(mask);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -236,7 +238,7 @@ void Terrain::SetLightMask(unsigned mask)
         if (patches_[i])
             patches_[i]->SetLightMask(mask);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -248,7 +250,7 @@ void Terrain::SetShadowMask(unsigned mask)
         if (patches_[i])
             patches_[i]->SetShadowMask(mask);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -260,7 +262,7 @@ void Terrain::SetZoneMask(unsigned mask)
         if (patches_[i])
             patches_[i]->SetZoneMask(mask);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -272,7 +274,7 @@ void Terrain::SetMaxLights(unsigned num)
         if (patches_[i])
             patches_[i]->SetMaxLights(num);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -284,7 +286,7 @@ void Terrain::SetCastShadows(bool enable)
         if (patches_[i])
             patches_[i]->SetCastShadows(enable);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -296,7 +298,7 @@ void Terrain::SetOccluder(bool enable)
         if (patches_[i])
             patches_[i]->SetOccluder(enable);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -308,7 +310,7 @@ void Terrain::SetOccludee(bool enable)
         if (patches_[i])
             patches_[i]->SetOccludee(enable);
     }
-    
+
     MarkNetworkUpdate();
 }
 
@@ -345,7 +347,7 @@ float Terrain::GetHeight(const Vector3& worldPosition) const
         float xFrac = xPos - floorf(xPos);
         float zFrac = zPos - floorf(zPos);
         float h1, h2, h3;
-        
+
         if (xFrac + zFrac >= 1.0f)
         {
             h1 = GetRawHeight((unsigned)xPos + 1, (unsigned)zPos + 1);
@@ -360,7 +362,7 @@ float Terrain::GetHeight(const Vector3& worldPosition) const
             h2 = GetRawHeight((unsigned)xPos + 1, (unsigned)zPos);
             h3 = GetRawHeight((unsigned)xPos, (unsigned)zPos + 1);
         }
-        
+
         float h = h1 * (1.0f - xFrac - zFrac) + h2 * xFrac + h3 * zFrac;
         /// \todo This assumes that the terrain scene node is upright
         return node_->GetWorldScale().y_ * h + node_->GetWorldPosition().y_;
@@ -379,7 +381,7 @@ Vector3 Terrain::GetNormal(const Vector3& worldPosition) const
         float xFrac = xPos - floorf(xPos);
         float zFrac = zPos - floorf(zPos);
         Vector3 n1, n2, n3;
-        
+
         if (xFrac + zFrac >= 1.0f)
         {
             n1 = GetRawNormal((unsigned)xPos + 1, (unsigned)zPos + 1);
@@ -394,7 +396,7 @@ Vector3 Terrain::GetNormal(const Vector3& worldPosition) const
             n2 = GetRawNormal((unsigned)xPos + 1, (unsigned)zPos);
             n3 = GetRawNormal((unsigned)xPos, (unsigned)zPos + 1);
         }
-        
+
         Vector3 n = (n1 * (1.0f - xFrac - zFrac) + n2 * xFrac + n3 * zFrac).Normalized();
         return node_->GetWorldRotation() * n;
     }
@@ -405,33 +407,33 @@ Vector3 Terrain::GetNormal(const Vector3& worldPosition) const
 void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 {
     PROFILE(CreatePatchGeometry);
-    
+
     unsigned row = patchSize_ + 1;
     VertexBuffer* vertexBuffer = patch->GetVertexBuffer();
     Geometry* geometry = patch->GetGeometry();
     Geometry* maxLodGeometry = patch->GetMaxLodGeometry();
     Geometry* minLodGeometry = patch->GetMinLodGeometry();
-    
+
     if (vertexBuffer->GetVertexCount() != row * row)
         vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
-    
+
     SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
-    
+
     float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount());
     float* positionData = (float*)cpuVertexData.Get();
     BoundingBox box;
-    
+
     if (vertexData)
     {
         const IntVector2& coords = patch->GetCoordinates();
-        
+
         for (int z1 = 0; z1 <= patchSize_; ++z1)
         {
             for (int x1 = 0; x1 <= patchSize_; ++x1)
             {
                 int xPos = coords.x_ * patchSize_ + x1;
                 int zPos = coords.y_ * patchSize_ + z1;
-                
+
                 // Position
                 Vector3 position((float)x1 * spacing_.x_, GetRawHeight(xPos, zPos), (float)z1 * spacing_.z_);
                 *vertexData++ = position.x_;
@@ -440,20 +442,20 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
                 *positionData++ = position.x_;
                 *positionData++ = position.y_;
                 *positionData++ = position.z_;
-                
+
                 box.Merge(position);
-                
+
                 // Normal
                 Vector3 normal = GetRawNormal(xPos, zPos);
                 *vertexData++ = normal.x_;
                 *vertexData++ = normal.y_;
                 *vertexData++ = normal.z_;
-                
+
                 // Texture coordinate
                 Vector2 texCoord((float)xPos / (float)numVertices_.x_, 1.0f - (float)zPos / (float)numVertices_.y_);
                 *vertexData++ = texCoord.x_;
                 *vertexData++ = texCoord.y_;
-                
+
                 // Tangent
                 Vector3 xyz = (Vector3::RIGHT - normal * normal.DotProduct(Vector3::RIGHT)).Normalized();
                 *vertexData++ = xyz.x_;
@@ -462,17 +464,17 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
                 *vertexData++ = 1.0f;
             }
         }
-        
+
         vertexBuffer->Unlock();
         vertexBuffer->ClearDataLost();
     }
-    
+
     patch->SetBoundingBox(box);
-    
+
     if (drawRanges_.Size())
     {
         unsigned lastDrawRange = drawRanges_.Size() - 1;
-        
+
         geometry->SetIndexBuffer(indexBuffer_);
         geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
         geometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION);
@@ -483,7 +485,7 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
         minLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[lastDrawRange].first_, drawRanges_[lastDrawRange].second_, false);
         minLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION);
     }
-    
+
     // Offset the occlusion geometry by vertex spacing to reduce possibility of over-aggressive occlusion
     patch->SetOcclusionOffset(-0.5f * (spacing_.x_ + spacing_.z_));
     patch->ResetLod();
@@ -492,7 +494,7 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 void Terrain::UpdatePatchLod(TerrainPatch* patch)
 {
     Geometry* geometry = patch->GetGeometry();
-    
+
     // All LOD levels except the coarsest have 16 versions for stitching
     unsigned lodLevel = patch->GetLodLevel();
     unsigned drawRangeIndex = lodLevel << 4;
@@ -502,7 +504,7 @@ void Terrain::UpdatePatchLod(TerrainPatch* patch)
         TerrainPatch* south = patch->GetSouthPatch();
         TerrainPatch* west = patch->GetWestPatch();
         TerrainPatch* east = patch->GetEastPatch();
-        
+
         if (north && north->GetLodLevel() > lodLevel)
             drawRangeIndex |= STITCH_NORTH;
         if (south && south->GetLodLevel() > lodLevel)
@@ -512,7 +514,7 @@ void Terrain::UpdatePatchLod(TerrainPatch* patch)
         if (east && east->GetLodLevel() > lodLevel)
             drawRangeIndex |= STITCH_EAST;
     }
-    
+
     if (drawRangeIndex < drawRanges_.Size())
         geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[drawRangeIndex].first_, drawRanges_[drawRangeIndex].second_, false);
 }
@@ -534,7 +536,7 @@ void Terrain::SetPatchSizeAttr(int value)
 {
     if (value < MIN_PATCH_SIZE || value > MAX_PATCH_SIZE || !IsPowerOfTwo(value))
         return;
-    
+
     if (value != patchSize_)
     {
         patchSize_ = value;
@@ -555,14 +557,14 @@ ResourceRef Terrain::GetHeightMapAttr() const
 void Terrain::CreateGeometry()
 {
     recreateTerrain_ = false;
-    
+
     if (!node_)
         return;
-    
+
     PROFILE(CreateTerrainGeometry);
-    
+
     unsigned prevNumPatches = patches_.Size();
-    
+
     // Determine number of LOD levels
     unsigned lodSize = patchSize_;
     numLodLevels_ = 1;
@@ -571,7 +573,7 @@ void Terrain::CreateGeometry()
         lodSize >>= 1;
         ++numLodLevels_;
     }
-    
+
     // Determine total terrain size
     patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_);
     if (heightMap_)
@@ -589,7 +591,7 @@ void Terrain::CreateGeometry()
         patchWorldOrigin_ = Vector2::ZERO;
         heightData_.Reset();
     }
-    
+
     // Remove old patch nodes which are not needed
     PODVector<Node*> oldPatchNodes;
     node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes);
@@ -604,13 +606,13 @@ void Terrain::CreateGeometry()
             if (x < numPatches_.x_ && z < numPatches_.y_)
                 nodeOk = true;
         }
-        
+
         if (!nodeOk)
             node_->RemoveChild(*i);
     }
-    
+
     patches_.Clear();
-    
+
     if (heightMap_)
     {
         // Copy heightmap data
@@ -618,7 +620,7 @@ void Terrain::CreateGeometry()
         float* dest = heightData_;
         unsigned imgComps = heightMap_->GetComponents();
         unsigned imgRow = heightMap_->GetWidth() * imgComps;
-        
+
         if (imgComps == 1)
         {
             for (int z = 0; z < numVertices_.y_; ++z)
@@ -651,14 +653,14 @@ void Terrain::CreateGeometry()
                 Node* patchNode = node_->GetChild(nodeName);
                 if (!patchNode)
                     patchNode = node_->CreateChild(nodeName, LOCAL);
-                
+
                 patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f, patchWorldOrigin_.y_ +
                     (float)z * patchWorldSize_.y_));
-                
+
                 TerrainPatch* patch = patchNode->GetOrCreateComponent<TerrainPatch>();
                 patch->SetOwner(this);
                 patch->SetCoordinates(IntVector2(x, z));
-                
+
                 // Copy initial drawable parameters
                 patch->SetEnabled(enabled);
                 patch->SetMaterial(material_);
@@ -673,14 +675,14 @@ void Terrain::CreateGeometry()
                 patch->SetCastShadows(castShadows_);
                 patch->SetOccluder(occluder_);
                 patch->SetOccludee(occludee_);
-                
+
                 patches_.Push(WeakPtr<TerrainPatch>(patch));
             }
         }
-        
+
         // Create the shared index data
         CreateIndexData();
-        
+
         // Create vertex data for patches
         for (Vector<WeakPtr<TerrainPatch> >::Iterator i = patches_.Begin(); i != patches_.End(); ++i)
         {
@@ -689,12 +691,12 @@ void Terrain::CreateGeometry()
             SetNeighbors(*i);
         }
     }
-    
+
     // Send event only if new geometry was generated, or the old was cleared
     if (patches_.Size() || prevNumPatches)
     {
         using namespace TerrainCreated;
-        
+
         VariantMap eventData;
         eventData[P_NODE] = (void*)node_;
         node_->SendEvent(E_TERRAINCREATED, eventData);
@@ -704,25 +706,25 @@ void Terrain::CreateGeometry()
 void Terrain::CreateIndexData()
 {
     PROFILE(CreateIndexData);
-    
+
     PODVector<unsigned short> indices;
     drawRanges_.Clear();
     unsigned row = patchSize_ + 1;
-    
+
     for (unsigned i = 0; i < numLodLevels_; ++i)
     {
         unsigned combinations = (i < numLodLevels_ - 1) ? 16 : 1;
         int skip = 1 << i;
-        
+
         for (unsigned j = 0; j < combinations; ++j)
         {
             unsigned indexStart = indices.Size();
-            
+
             int zStart = 0;
             int xStart = 0;
             int zEnd = patchSize_;
             int xEnd = patchSize_;
-            
+
             if (j & STITCH_NORTH)
                 zEnd -= skip;
             if (j & STITCH_SOUTH)
@@ -731,7 +733,7 @@ void Terrain::CreateIndexData()
                 xStart += skip;
             if (j & STITCH_EAST)
                 xEnd -= skip;
-            
+
             // Build the main grid
             for (int z = zStart; z < zEnd; z += skip)
             {
@@ -745,7 +747,7 @@ void Terrain::CreateIndexData()
                     indices.Push(z * row + x + skip);
                 }
             }
-            
+
             // Build the north edge
             if (j & STITCH_NORTH)
             {
@@ -769,7 +771,7 @@ void Terrain::CreateIndexData()
                     }
                 }
             }
-            
+
             // Build the south edge
             if (j & STITCH_SOUTH)
             {
@@ -793,7 +795,7 @@ void Terrain::CreateIndexData()
                     }
                 }
             }
-            
+
             // Build the west edge
             if (j & STITCH_WEST)
             {
@@ -817,7 +819,7 @@ void Terrain::CreateIndexData()
                     }
                 }
             }
-            
+
             // Build the east edge
             if (j & STITCH_EAST)
             {
@@ -841,11 +843,11 @@ void Terrain::CreateIndexData()
                     }
                 }
             }
-            
+
             drawRanges_.Push(MakePair(indexStart, indices.Size() - indexStart));
         }
     }
-    
+
     indexBuffer_->SetSize(indices.Size(), false);
     unsigned short* indexData = (unsigned short*)indexBuffer_->Lock(0, indices.Size());
     if (indexData)
@@ -859,7 +861,7 @@ float Terrain::GetRawHeight(int x, int z) const
 {
     if (!heightData_)
         return 0.0f;
-    
+
     x = Clamp(x, 0, numVertices_.x_ - 1);
     z = Clamp(z, 0, numVertices_.y_ - 1);
     return heightData_[z * numVertices_.x_ + x];
@@ -872,7 +874,7 @@ float Terrain::GetLodHeight(int x, int z, unsigned lodLevel) const
     float xFrac = (float)(x % offset) / divisor;
     float zFrac = (float)(z % offset) / divisor;
     float h1, h2, h3;
-    
+
     if (xFrac + zFrac >= 1.0f)
     {
         h1 = GetRawHeight(x + offset, z + offset);
@@ -887,7 +889,7 @@ float Terrain::GetLodHeight(int x, int z, unsigned lodLevel) const
         h2 = GetRawHeight(x + offset, z);
         h3 = GetRawHeight(x, z + offset);
     }
-    
+
     return h1 * (1.0f - xFrac - zFrac) + h2 * xFrac + h3 * zFrac;
 }
 
@@ -903,13 +905,13 @@ Vector3 Terrain::GetRawNormal(int x, int z) const
     float wSlope = GetRawHeight(x - 1, z) - baseHeight;
     float nwSlope = GetRawHeight(x - 1, z - 1) - baseHeight;
     float up = 0.5f * (spacing_.x_ + spacing_.z_);
-    
+
     return (Vector3(0.0f, up, nSlope) +
         Vector3(-neSlope, up, neSlope) +
         Vector3(-eSlope, up, 0.0f) +
         Vector3(-seSlope, up, -seSlope) +
         Vector3(0.0f, up, -sSlope) +
-        Vector3(swSlope, up, -swSlope) + 
+        Vector3(swSlope, up, -swSlope) +
         Vector3(wSlope, up, 0.0f) +
         Vector3(nwSlope, up, nwSlope)).Normalized();
 }
@@ -917,22 +919,22 @@ Vector3 Terrain::GetRawNormal(int x, int z) const
 void Terrain::CalculateLodErrors(TerrainPatch* patch)
 {
     PROFILE(CalculateLodErrors);
-    
+
     const IntVector2& coords = patch->GetCoordinates();
     PODVector<float>& lodErrors = patch->GetLodErrors();
     lodErrors.Clear();
     lodErrors.Reserve(numLodLevels_);
-    
+
     int xStart = coords.x_ * patchSize_;
     int zStart = coords.y_ * patchSize_;
     int xEnd = xStart + patchSize_;
     int zEnd = zStart + patchSize_;
-    
+
     for (unsigned i = 0; i < numLodLevels_; ++i)
     {
         float maxError = 0.0f;
         int divisor = 1 << i;
-        
+
         if (i > 0)
         {
             for (int z = zStart; z <= zEnd; ++z)
@@ -946,11 +948,11 @@ void Terrain::CalculateLodErrors(TerrainPatch* patch)
                     }
                 }
             }
-            
+
             // Set error to be at least same as (half vertex spacing x LOD) to prevent horizontal stretches getting too inaccurate
             maxError = Max(maxError, 0.25f * (spacing_.x_ + spacing_.z_) * (float)(1 << i));
         }
-        
+
         lodErrors.Push(maxError);
     }
 }
@@ -969,20 +971,20 @@ bool Terrain::SetHeightMapInternal(Image* image, bool recreateNow)
         LOGERROR("Can not use a compressed image as a terrain heightmap");
         return false;
     }
-    
+
     // Unsubscribe from the reload event of previous image (if any), then subscribe to the new
     if (heightMap_)
         UnsubscribeFromEvent(heightMap_, E_RELOADFINISHED);
     if (image)
         SubscribeToEvent(image, E_RELOADFINISHED, HANDLER(Terrain, HandleHeightMapReloadFinished));
-    
+
     heightMap_ = image;
-    
+
     if (recreateNow)
         CreateGeometry();
     else
         recreateTerrain_ = true;
-    
+
     return true;
 }
 

+ 9 - 7
Engine/Graphics/Terrain.h

@@ -27,6 +27,8 @@
 namespace Urho3D
 {
 
+extern const char* TERRAIN_CATEGORY;
+
 class Image;
 class Material;
 class Node;
@@ -36,7 +38,7 @@ class TerrainPatch;
 class Terrain : public Component
 {
     OBJECT(Terrain);
-    
+
 public:
     /// Construct.
     Terrain(Context* context);
@@ -44,14 +46,14 @@ public:
     ~Terrain();
     /// Register object factory.
     static void RegisterObject(Context* context);
-    
+
     /// Handle attribute write access.
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     virtual void ApplyAttributes();
     /// Handle enabled/disabled state change.
     virtual void OnSetEnabled();
-    
+
     /// Set patch quads per side. Must be a power of two.
     void SetPatchSize(int size);
     /// Set vertex (XZ) and height (Y) spacing.
@@ -82,7 +84,7 @@ public:
     void SetOccluder(bool enable);
     /// Set occludee flag for patches.
     void SetOccludee(bool enable);
-    
+
     /// Return patch quads per side.
     int GetPatchSize() const { return patchSize_; }
     /// Return vertex and height spacing.
@@ -129,7 +131,7 @@ public:
     bool IsOccluder() const { return occluder_; }
     /// Return occludee flag.
     bool IsOccludee() const { return occludee_; }
-    
+
     /// Regenerate patch geometry.
     void CreatePatchGeometry(TerrainPatch* patch);
     /// Update patch based on LOD and neighbor LOD.
@@ -144,7 +146,7 @@ public:
     ResourceRef GetHeightMapAttr() const;
     /// Return material attribute.
     ResourceRef GetMaterialAttr() const;
-    
+
 private:
     /// Fully regenerate terrain geometry.
     void CreateGeometry();
@@ -164,7 +166,7 @@ private:
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     /// Handle heightmap image reload finished.
     void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
-    
+
     /// Shared index buffer.
     SharedPtr<IndexBuffer> indexBuffer_;
     /// Heightmap image.

+ 1 - 1
Engine/Graphics/TerrainPatch.cpp

@@ -68,7 +68,7 @@ TerrainPatch::~TerrainPatch()
 
 void TerrainPatch::RegisterObject(Context* context)
 {
-    context->RegisterFactory<TerrainPatch>();
+    context->RegisterComponentFactory<TerrainPatch>(TERRAIN_CATEGORY);
 }
 
 void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)

+ 1 - 1
Engine/Graphics/Zone.cpp

@@ -62,7 +62,7 @@ Zone::~Zone()
 
 void Zone::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Zone>();
+    context->RegisterComponentFactory<Zone>(SCENE_CATEGORY);
 
     ACCESSOR_ATTRIBUTE(Zone, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Min", boundingBox_.min_, DEFAULT_BOUNDING_BOX_MIN, AM_DEFAULT);

+ 4 - 2
Engine/Navigation/Navigable.cpp

@@ -29,6 +29,8 @@
 namespace Urho3D
 {
 
+extern const char* NAVIGATION_CATEGORY;
+
 OBJECTTYPESTATIC(Navigable);
 
 Navigable::Navigable(Context* context) :
@@ -43,8 +45,8 @@ Navigable::~Navigable()
 
 void Navigable::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Navigable>();
-    
+    context->RegisterComponentFactory<Navigable>(NAVIGATION_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(Navigable, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ATTRIBUTE(Navigable, VAR_BOOL, "Recursive", recursive_, true, AM_DEFAULT);
 }

+ 3 - 3
Engine/Navigation/Navigable.h

@@ -31,7 +31,7 @@ namespace Urho3D
 class Navigable : public Component
 {
     OBJECT(Navigable);
-    
+
 public:
     /// Construct.
     Navigable(Context* context);
@@ -39,12 +39,12 @@ public:
     virtual ~Navigable();
     /// Register object factory.
     static void RegisterObject(Context* context);
-    
+
     /// Set whether geometry is automatically collected from child nodes. Default true.
     void SetRecursive(bool enable);
     /// Return whether geometry is automatically collected from child nodes.
     bool IsRecursive() const { return recursive_; }
-    
+
 private:
     /// Recursive flag.
     bool recursive_;

+ 2 - 0
Engine/Navigation/Navigation.cpp

@@ -30,6 +30,8 @@
 namespace Urho3D
 {
 
+const char* NAVIGATION_CATEGORY = "Navigation";
+
 void RegisterNavigationLibrary(Context* context)
 {
     Navigable::RegisterObject(context);

+ 2 - 0
Engine/Navigation/Navigation.h

@@ -25,6 +25,8 @@
 namespace Urho3D
 {
 
+extern const char* NAVIGATION_CATEGORY;
+
 class Context;
 
 /// Register Navigation library objects.

+ 3 - 1
Engine/Navigation/NavigationMesh.cpp

@@ -49,6 +49,8 @@
 namespace Urho3D
 {
 
+extern const char* NAVIGATION_CATEGORY;
+
 static const int DEFAULT_TILE_SIZE = 128;
 static const float DEFAULT_CELL_SIZE = 0.3f;
 static const float DEFAULT_CELL_HEIGHT = 0.2f;
@@ -180,7 +182,7 @@ NavigationMesh::~NavigationMesh()
 
 void NavigationMesh::RegisterObject(Context* context)
 {
-    context->RegisterFactory<NavigationMesh>();
+    context->RegisterComponentFactory<NavigationMesh>(NAVIGATION_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_INT, "Tile Size", GetTileSize, SetTileSize, int, DEFAULT_TILE_SIZE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Cell Size", GetCellSize, SetCellSize, float, DEFAULT_CELL_SIZE, AM_DEFAULT);

+ 3 - 1
Engine/Navigation/OffMeshConnection.cpp

@@ -31,6 +31,8 @@
 namespace Urho3D
 {
 
+extern const char* NAVIGATION_CATEGORY;
+
 OBJECTTYPESTATIC(OffMeshConnection);
 
 static const float DEFAULT_RADIUS = 1.0f;
@@ -50,7 +52,7 @@ OffMeshConnection::~OffMeshConnection()
 
 void OffMeshConnection::RegisterObject(Context* context)
 {
-    context->RegisterFactory<OffMeshConnection>();
+    context->RegisterComponentFactory<OffMeshConnection>(NAVIGATION_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(OffMeshConnection, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ATTRIBUTE(OffMeshConnection, VAR_INT, "Endpoint NodeID", endPointID_, 0, AM_DEFAULT | AM_NODEID);

+ 3 - 1
Engine/Network/NetworkPriority.cpp

@@ -29,6 +29,8 @@
 namespace Urho3D
 {
 
+const char* NETWORK_CATEGORY = "Network";
+
 static const float DEFAULT_BASE_PRIORITY = 100.0f;
 static const float DEFAULT_DISTANCE_FACTOR = 0.0f;
 static const float DEFAULT_MIN_PRIORITY = 0.0f;
@@ -51,7 +53,7 @@ NetworkPriority::~NetworkPriority()
 
 void NetworkPriority::RegisterObject(Context* context)
 {
-    context->RegisterFactory<NetworkPriority>();
+    context->RegisterComponentFactory<NetworkPriority>(NETWORK_CATEGORY);
     
     ATTRIBUTE(NetworkPriority, VAR_FLOAT, "Base Priority", basePriority_, DEFAULT_BASE_PRIORITY, AM_DEFAULT);
     ATTRIBUTE(NetworkPriority, VAR_FLOAT, "Distance Factor", distanceFactor_, DEFAULT_DISTANCE_FACTOR, AM_DEFAULT);

+ 1 - 1
Engine/Physics/CollisionShape.cpp

@@ -271,7 +271,7 @@ CollisionShape::~CollisionShape()
 
 void CollisionShape::RegisterObject(Context* context)
 {
-    context->RegisterFactory<CollisionShape>();
+    context->RegisterComponentFactory<CollisionShape>(PHYSICS_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(CollisionShape, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ENUM_ATTRIBUTE(CollisionShape, "Shape Type", shapeType_, typeNames, SHAPE_BOX, AM_DEFAULT);

+ 1 - 1
Engine/Physics/Constraint.cpp

@@ -80,7 +80,7 @@ Constraint::~Constraint()
 
 void Constraint::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Constraint>();
+    context->RegisterComponentFactory<Constraint>(PHYSICS_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(Constraint, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ENUM_ATTRIBUTE(Constraint, "Constraint Type", constraintType_, typeNames, CONSTRAINT_POINT, AM_DEFAULT);

+ 86 - 84
Engine/Physics/PhysicsWorld.cpp

@@ -47,6 +47,8 @@
 namespace Urho3D
 {
 
+const char* PHYSICS_CATEGORY = "Physics";
+
 static const int DEFAULT_FPS = 60;
 static const Vector3 DEFAULT_GRAVITY = Vector3(0.0f, -9.81f, 0.0f);
 
@@ -72,7 +74,7 @@ struct PhysicsQueryCallback : public btCollisionWorld::ContactResultCallback
     PhysicsQueryCallback(PODVector<RigidBody*>& result) : result_(result)
     {
     }
-    
+
     /// Add a contact result.
     virtual btScalar addSingleResult(btManifoldPoint &, const btCollisionObject *colObj0, int, int, const btCollisionObject *colObj1, int, int)
     {
@@ -84,7 +86,7 @@ struct PhysicsQueryCallback : public btCollisionWorld::ContactResultCallback
             result_.Push(body);
         return 0.0f;
     }
-    
+
     /// Found rigid bodies.
     PODVector<RigidBody*>& result_;
 };
@@ -111,7 +113,7 @@ PhysicsWorld::PhysicsWorld(Context* context) :
     broadphase_ = new btDbvtBroadphase();
     solver_ = new btSequentialImpulseConstraintSolver();
     world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
-    
+
     world_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
     world_->getDispatchInfo().m_useContinuous = true;
     world_->setDebugDrawer(this);
@@ -126,34 +128,34 @@ PhysicsWorld::~PhysicsWorld()
         // Force all remaining constraints, rigid bodies and collision shapes to release themselves
         for (PODVector<Constraint*>::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i)
             (*i)->ReleaseConstraint();
-        
+
         for (PODVector<RigidBody*>::Iterator i = rigidBodies_.Begin(); i != rigidBodies_.End(); ++i)
             (*i)->ReleaseBody();
-        
+
         for (PODVector<CollisionShape*>::Iterator i = collisionShapes_.Begin(); i != collisionShapes_.End(); ++i)
             (*i)->ReleaseShape();
     }
-    
+
     delete world_;
     world_ = 0;
-    
+
     delete solver_;
     solver_ = 0;
-    
+
     delete broadphase_;
     broadphase_ = 0;
-    
+
     delete collisionDispatcher_;
     collisionDispatcher_ = 0;
-    
+
     delete collisionConfiguration_;
     collisionConfiguration_ = 0;
 }
 
 void PhysicsWorld::RegisterObject(Context* context)
 {
-    context->RegisterFactory<PhysicsWorld>();
-    
+    context->RegisterComponentFactory<PhysicsWorld>(PHYSICS_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_VECTOR3, "Gravity", GetGravity, SetGravity, Vector3, DEFAULT_GRAVITY, AM_DEFAULT);
     ATTRIBUTE(PhysicsWorld, VAR_INT, "Physics FPS", fps_, DEFAULT_FPS, AM_DEFAULT);
     ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Net Max Angular Vel.", maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT);
@@ -179,7 +181,7 @@ void PhysicsWorld::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     if (debug)
     {
         PROFILE(PhysicsDrawDebug);
-        
+
         debugRenderer_ = debug;
         debugDepthTest_ = depthTest;
         world_->debugDrawWorld();
@@ -195,10 +197,10 @@ void PhysicsWorld::reportErrorWarning(const char* warningString)
 void PhysicsWorld::Update(float timeStep)
 {
     PROFILE(UpdatePhysics);
-    
+
     float internalTimeStep = 1.0f / fps_;
     delayedWorldTransforms_.Clear();
-    
+
     if (interpolation_)
     {
         int maxSubSteps = (int)(timeStep * fps_) + 1;
@@ -213,7 +215,7 @@ void PhysicsWorld::Update(float timeStep)
             timeAcc_ -= internalTimeStep;
         }
     }
-    
+
     // Apply delayed (parented) world transforms now
     while (!delayedWorldTransforms_.Empty())
     {
@@ -221,7 +223,7 @@ void PhysicsWorld::Update(float timeStep)
             i != delayedWorldTransforms_.End(); ++i)
         {
             const DelayedWorldTransform& transform = i->second_;
-            
+
             // If parent's transform has already been assigned, can proceed
             if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
             {
@@ -260,14 +262,14 @@ void PhysicsWorld::SetMaxNetworkAngularVelocity(float velocity)
 void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask)
 {
     PROFILE(PhysicsRaycast);
-    
+
     btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
         maxDistance * ray.direction_));
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterMask = collisionMask;
-    
+
     world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);
-    
+
     for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i)
     {
         PhysicsRaycastResult newResult;
@@ -277,21 +279,21 @@ void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& r
         newResult.distance_ = (newResult.position_ - ray.origin_).Length();
         result.Push(newResult);
     }
-    
+
     Sort(result.Begin(), result.End(), CompareRaycastResults);
 }
 
 void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask)
 {
     PROFILE(PhysicsRaycastSingle);
-    
+
     btCollisionWorld::ClosestRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
         maxDistance * ray.direction_));
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterMask = collisionMask;
-    
+
     world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);
-    
+
     if (rayCallback.hasHit())
     {
         result.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
@@ -311,17 +313,17 @@ void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, f
 void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask)
 {
     PROFILE(PhysicsSphereCast);
-    
+
     btSphereShape shape(radius);
-    
+
     btCollisionWorld::ClosestConvexResultCallback convexCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
         maxDistance * ray.direction_));
     convexCallback.m_collisionFilterGroup = (short)0xffff;
     convexCallback.m_collisionFilterMask = collisionMask;
-    
+
     world_->convexSweepTest(&shape, btTransform(btQuaternion::getIdentity(), convexCallback.m_convexFromWorld),
         btTransform(btQuaternion::getIdentity(), convexCallback.m_convexToWorld), convexCallback);
-    
+
     if (convexCallback.hasHit())
     {
         result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
@@ -341,19 +343,19 @@ void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, floa
 void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask)
 {
     PROFILE(PhysicsSphereQuery);
-    
+
     result.Clear();
-    
+
     btSphereShape sphereShape(sphere.radius_);
     btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &sphereShape);
     tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(sphere.center_)));
     // Need to activate the temporary rigid body to get reliable results from static, sleeping objects
     tempRigidBody->activate();
     world_->addRigidBody(tempRigidBody, (short)0xffff, (short)collisionMask);
-    
+
     PhysicsQueryCallback callback(result);
     world_->contactTest(tempRigidBody, callback);
-    
+
     world_->removeRigidBody(tempRigidBody);
     delete tempRigidBody;
 }
@@ -361,18 +363,18 @@ void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& s
 void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask)
 {
     PROFILE(PhysicsBoxQuery);
-    
+
     result.Clear();
-    
+
     btBoxShape boxShape(ToBtVector3(box.HalfSize()));
     btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &boxShape);
     tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(box.Center())));
     tempRigidBody->activate();
     world_->addRigidBody(tempRigidBody, (short)0xffff, (short)collisionMask);
-    
+
     PhysicsQueryCallback callback(result);
     world_->contactTest(tempRigidBody, callback);
-    
+
     world_->removeRigidBody(tempRigidBody);
     delete tempRigidBody;
 }
@@ -380,9 +382,9 @@ void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const BoundingB
 void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body)
 {
     PROFILE(GetCollidingBodies);
-    
+
     result.Clear();
-    
+
     for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
         i != currentCollisions_.End(); ++i)
     {
@@ -474,7 +476,7 @@ void PhysicsWorld::OnNodeSet(Node* node)
 void PhysicsWorld::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 {
     using namespace SceneSubsystemUpdate;
-    
+
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 
@@ -482,12 +484,12 @@ void PhysicsWorld::PreStep(float timeStep)
 {
     // Send pre-step event
     using namespace PhysicsPreStep;
-    
+
     VariantMap eventData;
     eventData[P_WORLD] = (void*)this;
     eventData[P_TIMESTEP] = timeStep;
     SendEvent(E_PHYSICSPRESTEP, eventData);
-    
+
     // Start profiling block for the actual simulation step
 #ifdef ENABLE_PROFILING
     Profiler* profiler = GetSubsystem<Profiler>();
@@ -503,12 +505,12 @@ void PhysicsWorld::PostStep(float timeStep)
     if (profiler)
         profiler->EndBlock();
 #endif
-    
+
     SendCollisionEvents();
-    
+
     // Send post-step event
     using namespace PhysicsPreStep;
-    
+
     VariantMap eventData;
     eventData[P_WORLD] = (void*)this;
     eventData[P_TIMESTEP] = timeStep;
@@ -518,18 +520,18 @@ void PhysicsWorld::PostStep(float timeStep)
 void PhysicsWorld::SendCollisionEvents()
 {
     PROFILE(SendCollisionEvents);
-    
+
     currentCollisions_.Clear();
     int numManifolds = collisionDispatcher_->getNumManifolds();
-    
+
     if (numManifolds)
     {
         VariantMap physicsCollisionData;
         VariantMap nodeCollisionData;
         VectorBuffer contacts;
-        
+
         physicsCollisionData[PhysicsCollision::P_WORLD] = (void*)this;
-        
+
         for (int i = 0; i < numManifolds; ++i)
         {
             btPersistentManifold* contactManifold = collisionDispatcher_->getManifoldByIndexInternal(i);
@@ -537,16 +539,16 @@ void PhysicsWorld::SendCollisionEvents()
             // First check that there are actual contacts, as the manifold exists also when objects are close but not touching
             if (!numContacts)
                 continue;
-            
+
             btCollisionObject* objectA = static_cast<btCollisionObject*>(contactManifold->getBody0());
             btCollisionObject* objectB = static_cast<btCollisionObject*>(contactManifold->getBody1());
-            
+
             RigidBody* bodyA = static_cast<RigidBody*>(objectA->getUserPointer());
             RigidBody* bodyB = static_cast<RigidBody*>(objectB->getUserPointer());
             // If it's not a rigidbody, maybe a ghost object
             if (!bodyA || !bodyB)
                 continue;
-            
+
             // Skip collision event signaling if both objects are static, or if collision event mode does not match
             if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
                 continue;
@@ -555,21 +557,21 @@ void PhysicsWorld::SendCollisionEvents()
             if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
                 !bodyA->IsActive() && !bodyB->IsActive())
                 continue;
-            
+
             WeakPtr<RigidBody> bodyWeakA(bodyA);
             WeakPtr<RigidBody> bodyWeakB(bodyB);
-            
+
             Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
             if (bodyA < bodyB)
                 bodyPair = MakePair(bodyWeakA, bodyWeakB);
             else
                 bodyPair = MakePair(bodyWeakB, bodyWeakA);
-            
+
             // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
             // objects during collision event handling
             currentCollisions_[bodyPair] = contactManifold;
         }
-        
+
         for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
             i != currentCollisions_.End(); ++i)
         {
@@ -577,26 +579,26 @@ void PhysicsWorld::SendCollisionEvents()
             RigidBody* bodyB = i->first_.second_;
             if (!bodyA || !bodyB)
                 continue;
-            
+
             btPersistentManifold* contactManifold = i->second_;
             int numContacts = contactManifold->getNumContacts();
-            
+
             Node* nodeA = bodyA->GetNode();
             Node* nodeB = bodyB->GetNode();
             WeakPtr<Node> nodeWeakA(nodeA);
             WeakPtr<Node> nodeWeakB(nodeB);
-            
+
             bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
             bool newCollision = !previousCollisions_.Contains(i->first_);
-            
+
             physicsCollisionData[PhysicsCollision::P_NODEA] = (void*)nodeA;
             physicsCollisionData[PhysicsCollision::P_NODEB] = (void*)nodeB;
             physicsCollisionData[PhysicsCollision::P_BODYA] = (void*)bodyA;
             physicsCollisionData[PhysicsCollision::P_BODYB] = (void*)bodyB;
             physicsCollisionData[PhysicsCollision::P_PHANTOM] = phantom;
-            
+
             contacts.Clear();
-            
+
             for (int j = 0; j < numContacts; ++j)
             {
                 btManifoldPoint& point = contactManifold->getContactPoint(j);
@@ -605,9 +607,9 @@ void PhysicsWorld::SendCollisionEvents()
                 contacts.WriteFloat(point.m_distance1);
                 contacts.WriteFloat(point.m_appliedImpulse);
             }
-            
+
             physicsCollisionData[PhysicsCollision::P_CONTACTS] = contacts.GetBuffer();
-            
+
             // Send separate collision start event if collision is new
             if (newCollision)
             {
@@ -616,29 +618,29 @@ void PhysicsWorld::SendCollisionEvents()
                 if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
             }
-            
+
             // Then send the ongoing collision event
             SendEvent(E_PHYSICSCOLLISION, physicsCollisionData);
             if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                 continue;
-            
+
             nodeCollisionData[NodeCollision::P_BODY] = (void*)bodyA;
             nodeCollisionData[NodeCollision::P_OTHERNODE] = (void*)nodeB;
             nodeCollisionData[NodeCollision::P_OTHERBODY] = (void*)bodyB;
             nodeCollisionData[NodeCollision::P_PHANTOM] = phantom;
             nodeCollisionData[NodeCollision::P_CONTACTS] = contacts.GetBuffer();
-            
+
             if (newCollision)
             {
                 nodeA->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
                 if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
             }
-            
+
             nodeA->SendEvent(E_NODECOLLISION, nodeCollisionData);
             if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                 continue;
-            
+
             contacts.Clear();
             for (int j = 0; j < numContacts; ++j)
             {
@@ -648,30 +650,30 @@ void PhysicsWorld::SendCollisionEvents()
                 contacts.WriteFloat(point.m_distance1);
                 contacts.WriteFloat(point.m_appliedImpulse);
             }
-            
+
             nodeCollisionData[NodeCollision::P_BODY] = (void*)bodyB;
             nodeCollisionData[NodeCollision::P_OTHERNODE] = (void*)nodeA;
             nodeCollisionData[NodeCollision::P_OTHERBODY] = (void*)bodyA;
             nodeCollisionData[NodeCollision::P_CONTACTS] = contacts.GetBuffer();
-            
+
             if (newCollision)
             {
                 nodeB->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
                 if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
             }
-            
+
             nodeB->SendEvent(E_NODECOLLISION, nodeCollisionData);
         }
     }
-    
+
     // Send collision end events as applicable
     {
         VariantMap physicsCollisionData;
         VariantMap nodeCollisionData;
-        
+
         physicsCollisionData[PhysicsCollisionEnd::P_WORLD] = (void*)this;
-        
+
         for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
         {
             if (!currentCollisions_.Contains(i->first_))
@@ -680,9 +682,9 @@ void PhysicsWorld::SendCollisionEvents()
                 RigidBody* bodyB = i->first_.second_;
                 if (!bodyA || !bodyB)
                     continue;
-                
+
                 bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
-                
+
                 // Skip collision event signaling if both objects are static, or if collision event mode does not match
                 if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
                     continue;
@@ -691,41 +693,41 @@ void PhysicsWorld::SendCollisionEvents()
                 if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
                     !bodyA->IsActive() && !bodyB->IsActive())
                     continue;
-                
+
                 Node* nodeA = bodyA->GetNode();
                 Node* nodeB = bodyB->GetNode();
                 WeakPtr<Node> nodeWeakA(nodeA);
                 WeakPtr<Node> nodeWeakB(nodeB);
-                
+
                 physicsCollisionData[PhysicsCollisionEnd::P_BODYA] = (void*)bodyA;
                 physicsCollisionData[PhysicsCollisionEnd::P_BODYB] = (void*)bodyB;
                 physicsCollisionData[PhysicsCollisionEnd::P_NODEA] = (void*)nodeA;
                 physicsCollisionData[PhysicsCollisionEnd::P_NODEB] = (void*)nodeB;
                 physicsCollisionData[PhysicsCollisionEnd::P_PHANTOM] = phantom;
-                
+
                 SendEvent(E_PHYSICSCOLLISIONEND, physicsCollisionData);
                 // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
                 if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
-                
+
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyA;
                 nodeCollisionData[NodeCollisionEnd::P_OTHERNODE] = (void*)nodeB;
                 nodeCollisionData[NodeCollisionEnd::P_OTHERBODY] = (void*)bodyB;
                 nodeCollisionData[NodeCollisionEnd::P_PHANTOM] = phantom;
-                
+
                 nodeA->SendEvent(E_NODECOLLISIONEND, nodeCollisionData);
                 if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
-                
+
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyB;
                 nodeCollisionData[NodeCollisionEnd::P_OTHERNODE] = (void*)nodeA;
                 nodeCollisionData[NodeCollisionEnd::P_OTHERBODY] = (void*)bodyA;
-                
+
                 nodeB->SendEvent(E_NODECOLLISIONEND, nodeCollisionData);
             }
         }
     }
-    
+
     previousCollisions_ = currentCollisions_;
 }
 

+ 12 - 10
Engine/Physics/PhysicsWorld.h

@@ -39,6 +39,8 @@ class btPersistentManifold;
 namespace Urho3D
 {
 
+extern const char* PHYSICS_CATEGORY;
+
 class CollisionShape;
 class Deserializer;
 class Constraint;
@@ -58,7 +60,7 @@ struct PhysicsRaycastResult
         body_(0)
     {
     }
-    
+
     /// Hit position.
     Vector3 position_;
     /// Hit normal.
@@ -89,9 +91,9 @@ class PhysicsWorld : public Component, public btIDebugDraw
 {
     friend void InternalPreTickCallback(btDynamicsWorld *world, btScalar timeStep);
     friend void InternalTickCallback(btDynamicsWorld *world, btScalar timeStep);
-    
+
     OBJECT(PhysicsWorld);
-    
+
 public:
     /// Construct.
     PhysicsWorld(Context* scontext);
@@ -99,7 +101,7 @@ public:
     virtual ~PhysicsWorld();
     /// Register object factory.
     static void RegisterObject(Context* context);
-    
+
     /// Check if an AABB is visible for debug drawing.
     virtual bool isVisible(const btVector3& aabbMin, const btVector3& aabbMax);
     /// Draw a physics debug line.
@@ -116,7 +118,7 @@ public:
     virtual int getDebugMode() const { return debugMode_; }
     /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
-    
+
     /// Step the simulation forward.
     void Update(float timeStep);
     /// Refresh collisions only without updating dynamics.
@@ -149,7 +151,7 @@ public:
     int GetFps() const { return fps_; }
     /// Return maximum angular velocity for network replication.
     float GetMaxNetworkAngularVelocity() const { return maxNetworkAngularVelocity_; }
-    
+
     /// Add a rigid body to keep track of. Called by RigidBody.
     void AddRigidBody(RigidBody* body);
     /// Remove a rigid body. Called by RigidBody.
@@ -170,7 +172,7 @@ public:
     void SetDebugRenderer(DebugRenderer* debug);
     /// Set debug geometry depth test mode. Called both by PhysicsWorld itself and physics components.
     void SetDebugDepthTest(bool enable);
-    
+
     /// Return the Bullet physics world.
     btDiscreteDynamicsWorld* GetWorld() { return world_; }
     /// Clean up the geometry cache.
@@ -181,11 +183,11 @@ public:
     void SetApplyingTransforms(bool enable) { applyingTransforms_ = enable; }
     /// Return whether node dirtying should be disregarded.
     bool IsApplyingTransforms() const { return applyingTransforms_; }
-    
+
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
-    
+
 private:
     /// Handle the scene subsystem update event, step simulation here.
     void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
@@ -195,7 +197,7 @@ private:
     void PostStep(float timeStep);
     /// Send accumulated collision events.
     void SendCollisionEvents();
-    
+
     /// Bullet collision configuration.
     btCollisionConfiguration* collisionConfiguration_;
     /// Bullet collision dispatcher.

+ 47 - 47
Engine/Physics/RigidBody.cpp

@@ -49,7 +49,7 @@ static const float DEFAULT_RESTITUTION = 0.0f;
 static const unsigned DEFAULT_COLLISION_LAYER = 0x1;
 static const unsigned DEFAULT_COLLISION_MASK = M_MAX_UNSIGNED;
 
-static const char* collisionEventModeNames[] = 
+static const char* collisionEventModeNames[] =
 {
     "Never",
     "When Active",
@@ -83,18 +83,18 @@ RigidBody::RigidBody(Context* context) :
 RigidBody::~RigidBody()
 {
     ReleaseBody();
-    
+
     if (physicsWorld_)
         physicsWorld_->RemoveRigidBody(this);
-    
+
     delete compoundShape_;
     compoundShape_ = 0;
 }
 
 void RigidBody::RegisterObject(Context* context)
 {
-    context->RegisterFactory<RigidBody>();
-    
+    context->RegisterComponentFactory<RigidBody>(PHYSICS_CATEGORY);
+
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Physics Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_QUATERNION, "Physics Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_FILE | AM_NOEDIT);
@@ -106,9 +106,9 @@ void RigidBody::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Linear Factor", GetLinearFactor, SetLinearFactor, Vector3, Vector3::ONE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Angular Factor", GetAngularFactor, SetAngularFactor, Vector3, Vector3::ONE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.01f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.8f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 1.0f, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Layer", collisionLayer_, DEFAULT_COLLISION_LAYER, AM_DEFAULT);
     ATTRIBUTE(RigidBody, VAR_INT, "Collision Mask", collisionMask_, DEFAULT_COLLISION_MASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Contact Threshold", GetContactProcessingThreshold, SetContactProcessingThreshold, float, BT_LARGE_FLOAT, AM_DEFAULT);
@@ -125,7 +125,7 @@ void RigidBody::RegisterObject(Context* context)
 void RigidBody::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
     Component::OnSetAttribute(attr, src);
-    
+
     // Change of any non-accessor attribute requires the rigid body to be re-added to the physics world
     if (!attr.accessor_)
         readdBody_ = true;
@@ -140,7 +140,7 @@ void RigidBody::ApplyAttributes()
 void RigidBody::OnSetEnabled()
 {
     bool enabled = IsEnabledEffective();
-    
+
     if (enabled && !inWorld_)
         AddBodyToWorld();
     else if (!enabled && inWorld_)
@@ -165,7 +165,7 @@ void RigidBody::setWorldTransform(const btTransform &worldTrans)
     Vector3 newWorldPosition = ToVector3(worldTrans.getOrigin());
     Quaternion newWorldRotation = ToQuaternion(worldTrans.getRotation());
     RigidBody* parentRigidBody = 0;
-    
+
     // It is possible that the RigidBody component has been kept alive via a shared pointer,
     // while its scene node has already been destroyed
     if (node_)
@@ -175,7 +175,7 @@ void RigidBody::setWorldTransform(const btTransform &worldTrans)
         Node* parent = node_->GetParent();
         if (parent && parent != GetScene())
             parentRigidBody = parent->GetComponent<RigidBody>();
-        
+
         if (!parentRigidBody)
             ApplyWorldTransform(newWorldPosition, newWorldRotation);
         else
@@ -187,7 +187,7 @@ void RigidBody::setWorldTransform(const btTransform &worldTrans)
             delayed.worldRotation_ = newWorldRotation;
             physicsWorld_->AddDelayedWorldTransform(delayed);
         }
-        
+
         MarkNetworkUpdate();
     }
 }
@@ -198,11 +198,11 @@ void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     {
         physicsWorld_->SetDebugRenderer(debug);
         physicsWorld_->SetDebugDepthTest(depthTest);
-        
+
         btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
-        world->debugDrawObject(body_->getWorldTransform(), compoundShape_, IsActive() ? btVector3(1.0f, 1.0f, 1.0f) : 
+        world->debugDrawObject(body_->getWorldTransform(), compoundShape_, IsActive() ? btVector3(1.0f, 1.0f, 1.0f) :
             btVector3(0.0f, 1.0f, 0.0f));
-        
+
         physicsWorld_->SetDebugRenderer(0);
     }
 }
@@ -210,7 +210,7 @@ void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void RigidBody::SetMass(float mass)
 {
     mass = Max(mass, 0.0f);
-    
+
     if (mass != mass_)
     {
         mass_ = mass;
@@ -225,13 +225,13 @@ void RigidBody::SetPosition(Vector3 position)
     {
         btTransform& worldTrans = body_->getWorldTransform();
         worldTrans.setOrigin(ToBtVector3(position));
-        
+
         // When forcing the physics position, set also interpolated position so that there is no jitter
         btTransform interpTrans = body_->getInterpolationWorldTransform();
         interpTrans.setOrigin(worldTrans.getOrigin());
         body_->setInterpolationWorldTransform(interpTrans);
         body_->updateInertiaTensor();
-        
+
         Activate();
         MarkNetworkUpdate();
     }
@@ -243,13 +243,13 @@ void RigidBody::SetRotation(Quaternion rotation)
     {
         btTransform& worldTrans = body_->getWorldTransform();
         worldTrans.setRotation(ToBtQuaternion(rotation));
-        
+
         // When forcing the physics position, set also interpolated position so that there is no jitter
         btTransform interpTrans = body_->getInterpolationWorldTransform();
         interpTrans.setRotation(worldTrans.getRotation());
         body_->setInterpolationWorldTransform(interpTrans);
         body_->updateInertiaTensor();
-        
+
         Activate();
         MarkNetworkUpdate();
     }
@@ -262,14 +262,14 @@ void RigidBody::SetTransform(const Vector3& position, const Quaternion& rotation
         btTransform& worldTrans = body_->getWorldTransform();
         worldTrans.setOrigin(ToBtVector3(position));
         worldTrans.setRotation(ToBtQuaternion(rotation));
-        
+
         // When forcing the physics position, set also interpolated position so that there is no jitter
         btTransform interpTrans = body_->getInterpolationWorldTransform();
         interpTrans.setOrigin(worldTrans.getOrigin());
         interpTrans.setRotation(worldTrans.getRotation());
         body_->setInterpolationWorldTransform(interpTrans);
         body_->updateInertiaTensor();
-        
+
         Activate();
         MarkNetworkUpdate();
     }
@@ -680,12 +680,12 @@ void RigidBody::GetCollidingBodies(PODVector<RigidBody*>& result) const
 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
 {
     physicsWorld_->SetApplyingTransforms(true);
-    
+
     // Apply transform to the SmoothedTransform component instead of node transform if available
     SmoothedTransform* transform = 0;
     if (hasSmoothedTransform_)
         transform = GetComponent<SmoothedTransform>();
-    
+
     if (transform)
     {
         transform->SetTargetWorldPosition(newWorldPosition);
@@ -700,7 +700,7 @@ void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quate
         lastPosition_ = node_->GetWorldPosition();
         lastRotation_ = node_->GetWorldRotation();
     }
-    
+
     physicsWorld_->SetApplyingTransforms(false);
 }
 
@@ -721,14 +721,14 @@ void RigidBody::UpdateGravity()
     if (physicsWorld_ && body_)
     {
         btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
-        
+
         int flags = body_->getFlags();
         if (useGravity_ && gravityOverride_ == Vector3::ZERO)
             flags &= ~BT_DISABLE_WORLD_GRAVITY;
         else
             flags |= BT_DISABLE_WORLD_GRAVITY;
         body_->setFlags(flags);
-        
+
         if (useGravity_)
         {
             // If override vector is zero, use world's gravity
@@ -778,9 +778,9 @@ void RigidBody::ReleaseBody()
         PODVector<Constraint*> constraints = constraints_;
         for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i)
             (*i)->ReleaseConstraint();
-        
+
         RemoveBodyFromWorld();
-        
+
         delete body_;
         body_ = 0;
     }
@@ -800,11 +800,11 @@ void RigidBody::OnMarkedDirty(Node* node)
             scene->DelayedMarkedDirty(this);
             return;
         }
-        
+
         // Check if transform has changed from the last one set in ApplyWorldTransform()
         Vector3 newPosition = node_->GetWorldPosition();
         Quaternion newRotation = node_->GetWorldRotation();
-        
+
         if (!newPosition.Equals(lastPosition_))
         {
             lastPosition_ = newPosition;
@@ -827,13 +827,13 @@ void RigidBody::OnNodeSet(Node* node)
         {
             if (scene == node)
                 LOGWARNING(GetTypeName() + " should not be created to the root scene node");
-            
+
             physicsWorld_ = scene->GetComponent<PhysicsWorld>();
             if (physicsWorld_)
                 physicsWorld_->AddRigidBody(this);
             else
                 LOGERROR("No physics world component in scene, can not create rigid body");
-            
+
             AddBodyToWorld();
         }
         node->AddListener(this);
@@ -844,14 +844,14 @@ void RigidBody::AddBodyToWorld()
 {
     if (!physicsWorld_)
         return;
-    
+
     PROFILE(AddBodyToWorld);
-    
+
     if (mass_ < 0.0f)
         mass_ = 0.0f;
-    
+
     bool massUpdated = false;
-    
+
     if (body_)
         RemoveBodyFromWorld();
     else
@@ -860,7 +860,7 @@ void RigidBody::AddBodyToWorld()
         btVector3 localInertia(0.0f, 0.0f, 0.0f);
         body_ = new btRigidBody(mass_, this, compoundShape_, localInertia);
         body_->setUserPointer(this);
-        
+
         // Check for existence of the SmoothedTransform component, which should be created by now in network client mode.
         // If it exists, subscribe to its change events
         SmoothedTransform* transform = GetComponent<SmoothedTransform>();
@@ -870,7 +870,7 @@ void RigidBody::AddBodyToWorld()
             SubscribeToEvent(transform, E_TARGETPOSITION, HANDLER(RigidBody, HandleTargetPosition));
             SubscribeToEvent(transform, E_TARGETROTATION, HANDLER(RigidBody, HandleTargetRotation));
         }
-        
+
         // Check if CollisionShapes already exist in the node and add them to the compound shape.
         // Note: NotifyRigidBody() will cause mass to be updated
         PODVector<CollisionShape*> shapes;
@@ -880,7 +880,7 @@ void RigidBody::AddBodyToWorld()
             massUpdated = true;
             (*i)->NotifyRigidBody();
         }
-        
+
         // Check if this node contains Constraint components that were waiting for the rigid body to be created, and signal them
         // to create themselves now
         PODVector<Constraint*> constraints;
@@ -888,12 +888,12 @@ void RigidBody::AddBodyToWorld()
         for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i)
             (*i)->CreateConstraint();
     }
-    
+
     if (!massUpdated)
         UpdateMass();
-    
+
     UpdateGravity();
-    
+
     int flags = body_->getCollisionFlags();
     if (phantom_)
         flags |= btCollisionObject::CF_NO_CONTACT_RESPONSE;
@@ -904,15 +904,15 @@ void RigidBody::AddBodyToWorld()
     else
         flags &= ~btCollisionObject::CF_KINEMATIC_OBJECT;
     body_->setCollisionFlags(flags);
-    
+
     if (!IsEnabledEffective())
         return;
-    
+
     btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
     world->addRigidBody(body_, collisionLayer_, collisionMask_);
     inWorld_ = true;
     readdBody_ = false;
-    
+
     if (mass_ > 0.0f)
         Activate();
     else

+ 27 - 15
Engine/Scene/Component.cpp

@@ -60,7 +60,7 @@ bool Component::Save(Serializer& dest) const
         return false;
     if (!dest.WriteUInt(id_))
         return false;
-    
+
     // Write attributes
     return Serializable::Save(dest);
 }
@@ -72,7 +72,7 @@ bool Component::SaveXML(XMLElement& dest) const
         return false;
     if (!dest.SetInt("id", id_))
         return false;
-    
+
     // Write attributes
     return Serializable::SaveXML(dest);
 }
@@ -84,18 +84,18 @@ void Component::SetEnabled(bool enable)
         enabled_ = enable;
         OnSetEnabled();
         MarkNetworkUpdate();
-        
+
         // Send change event for the component
         Scene* scene = GetScene();
         if (scene)
         {
             using namespace ComponentEnabledChanged;
-            
+
             VariantMap eventData;
             eventData[P_SCENE] = (void*)scene;
             eventData[P_NODE] = (void*)node_;
             eventData[P_COMPONENT] = (void*)this;
-            
+
             scene->SendEvent(E_COMPONENTENABLEDCHANGED, eventData);
         }
     }
@@ -116,7 +116,7 @@ void Component::AddReplicationState(ComponentReplicationState* state)
 {
     if (!networkState_)
         AllocateNetworkState();
-    
+
     networkState_->replicationStates_.Push(state);
 }
 
@@ -124,40 +124,40 @@ void Component::PrepareNetworkUpdate()
 {
     if (!networkState_)
         AllocateNetworkState();
-    
+
     const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     if (!attributes)
         return;
-    
+
     unsigned numAttributes = attributes->Size();
-    
+
     if (networkState_->currentValues_.Size() != numAttributes)
     {
         networkState_->currentValues_.Resize(numAttributes);
         networkState_->previousValues_.Resize(numAttributes);
-        
+
         // Copy the default attribute values to the previous state as a starting point
         for (unsigned i = 0; i < numAttributes; ++i)
             networkState_->previousValues_[i] = attributes->At(i).defaultValue_;
     }
-    
+
     // Check for attribute changes
     for (unsigned i = 0; i < numAttributes; ++i)
     {
         const AttributeInfo& attr = attributes->At(i);
         OnGetAttribute(attr, networkState_->currentValues_[i]);
-        
+
         if (networkState_->currentValues_[i] != networkState_->previousValues_[i])
         {
             networkState_->previousValues_[i] = networkState_->currentValues_[i];
-            
+
             // Mark the attribute dirty in all replication states that are tracking this component
             for (PODVector<ReplicationState*>::Iterator j = networkState_->replicationStates_.Begin(); j !=
                 networkState_->replicationStates_.End(); ++j)
             {
                 ComponentReplicationState* compState = static_cast<ComponentReplicationState*>(*j);
                 compState->dirtyAttributes_.Set(i);
-                
+
                 // Add component's parent node to the dirty set if not added yet
                 NodeReplicationState* nodeState = compState->nodeState_;
                 if (!nodeState->markedDirty_)
@@ -168,7 +168,7 @@ void Component::PrepareNetworkUpdate()
             }
         }
     }
-    
+
     networkUpdate_ = false;
 }
 
@@ -197,6 +197,18 @@ void Component::MarkNetworkUpdate()
     }
 }
 
+const String& Component::GetCategory() const
+{
+    const HashMap<String, Vector<ShortStringHash> >& componentCategories = context_->GetComponentCategories();
+    for (HashMap<String, Vector<ShortStringHash> >::ConstIterator i = componentCategories.Begin(); i != componentCategories.End(); ++i)
+    {
+        if (i->second_.Contains(GetType()))
+            return i->first_;
+    }
+
+    return String::EMPTY;
+}
+
 void Component::SetID(unsigned id)
 {
     id_ = id;

+ 3 - 0
Engine/Scene/Component.h

@@ -93,6 +93,9 @@ public:
     /// Mark for attribute check on the next network update.
     void MarkNetworkUpdate();
     
+    /// Return component category. Return an empty string if the component category is not registered.
+    const String& GetCategory() const;
+
 protected:
     /// Handle scene node being assigned at creation.
     virtual void OnNodeSet(Node* node) {};

+ 3 - 0
Engine/Scene/Scene.cpp

@@ -40,6 +40,9 @@
 namespace Urho3D
 {
 
+const char* SCENE_CATEGORY = "Scene";
+const char* EFFECT_CATEGORY = "Effect";
+
 static const int ASYNC_LOAD_MIN_FPS = 30;
 static const int ASYNC_LOAD_MAX_MSEC = (int)(1000.0f / ASYNC_LOAD_MIN_FPS);
 static const float DEFAULT_SMOOTHING_CONSTANT = 50.0f;

+ 3 - 0
Engine/Scene/Scene.h

@@ -31,6 +31,9 @@
 namespace Urho3D
 {
 
+extern const char* SCENE_CATEGORY;
+extern const char* EFFECT_CATEGORY;
+
 class File;
 class PackageFile;
 

+ 14 - 12
Engine/Scene/SmoothedTransform.cpp

@@ -31,6 +31,8 @@
 namespace Urho3D
 {
 
+static const char* NETWORK_CATEGORY = "Network";
+
 OBJECTTYPESTATIC(SmoothedTransform);
 
 SmoothedTransform::SmoothedTransform(Context* context) :
@@ -48,7 +50,7 @@ SmoothedTransform::~SmoothedTransform()
 
 void SmoothedTransform::RegisterObject(Context* context)
 {
-    context->RegisterFactory<SmoothedTransform>();
+    context->RegisterComponentFactory<SmoothedTransform>(NETWORK_CATEGORY);
 }
 
 void SmoothedTransform::Update(float constant, float squaredSnapThreshold)
@@ -57,14 +59,14 @@ void SmoothedTransform::Update(float constant, float squaredSnapThreshold)
     {
         Vector3 position = node_->GetPosition();
         Quaternion rotation = node_->GetRotation();
-        
+
         if (smoothingMask_ & SMOOTH_POSITION)
         {
             // If position snaps, snap everything to the end
             float delta = (position - targetPosition_).LengthSquared();
             if (delta > squaredSnapThreshold)
                 constant = 1.0f;
-            
+
             if (delta < M_EPSILON || constant >= 1.0f)
             {
                 position = targetPosition_;
@@ -72,10 +74,10 @@ void SmoothedTransform::Update(float constant, float squaredSnapThreshold)
             }
             else
                 position = position.Lerp(targetPosition_, constant);
-            
+
             node_->SetPosition(position);
         }
-        
+
         if (smoothingMask_ & SMOOTH_ROTATION)
         {
             float delta = (rotation - targetRotation_).LengthSquared();
@@ -86,11 +88,11 @@ void SmoothedTransform::Update(float constant, float squaredSnapThreshold)
             }
             else
                 rotation = rotation.Slerp(targetRotation_, constant);
-            
+
             node_->SetRotation(rotation);
         }
     }
-    
+
     // If smoothing has completed, unsubscribe from the update event
     if (!smoothingMask_)
     {
@@ -103,14 +105,14 @@ void SmoothedTransform::SetTargetPosition(const Vector3& position)
 {
     targetPosition_ = position;
     smoothingMask_ |= SMOOTH_POSITION;
-    
+
     // Subscribe to smoothing update if not yet subscribed
     if (!subscribed_)
     {
         SubscribeToEvent(GetScene(), E_UPDATESMOOTHING, HANDLER(SmoothedTransform, HandleUpdateSmoothing));
         subscribed_ = true;
     }
-    
+
     SendEvent(E_TARGETPOSITION);
 }
 
@@ -118,13 +120,13 @@ void SmoothedTransform::SetTargetRotation(const Quaternion& rotation)
 {
     targetRotation_ = rotation;
     smoothingMask_ |= SMOOTH_ROTATION;
-    
+
     if (!subscribed_)
     {
         SubscribeToEvent(GetScene(), E_UPDATESMOOTHING, HANDLER(SmoothedTransform, HandleUpdateSmoothing));
         subscribed_ = true;
     }
-    
+
     SendEvent(E_TARGETROTATION);
 }
 
@@ -173,7 +175,7 @@ void SmoothedTransform::OnNodeSet(Node* node)
 void SmoothedTransform::HandleUpdateSmoothing(StringHash eventType, VariantMap& eventData)
 {
     using namespace UpdateSmoothing;
-    
+
     float constant = eventData[P_CONSTANT].GetFloat();
     float squaredSnapThreshold = eventData[P_SQUAREDSNAPTHRESHOLD].GetFloat();
     Update(constant, squaredSnapThreshold);

+ 47 - 45
Engine/Script/Script.cpp

@@ -37,6 +37,8 @@
 namespace Urho3D
 {
 
+const char* SCRIPT_CATEGORY = "Script";
+
 /// %Object property info for scripting API dump.
 struct PropertyInfo
 {
@@ -47,7 +49,7 @@ struct PropertyInfo
         indexed_(false)
     {
     }
-    
+
     /// Property name.
     String name_;
     /// Property data type.
@@ -96,7 +98,7 @@ void ExtractPropertyInfo(const String& functionName, const String& declaration,
             info->indexed_ = true;
             info->type_ += "[]";
         }
-        
+
         // Sanitate the reference operator away
         info->type_.Replace("&", "");
     }
@@ -111,7 +113,7 @@ void ExtractPropertyInfo(const String& functionName, const String& declaration,
                 begin = declaration.Find('(');
             else
                 info->indexed_ = true;
-            
+
             if (begin != String::NPOS)
             {
                 ++begin;
@@ -144,18 +146,18 @@ Script::Script(Context* context) :
         LOGERROR("Could not create AngelScript engine");
         return;
     }
-    
+
     scriptEngine_->SetUserData(this);
     scriptEngine_->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true);
     scriptEngine_->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
     scriptEngine_->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, true);
     scriptEngine_->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, true);
     scriptEngine_->SetMessageCallback(asMETHOD(Script, MessageCallback), this, asCALL_THISCALL);
-    
+
     // Create the context for immediate execution
     immediateContext_ = scriptEngine_->CreateContext();
     immediateContext_->SetExceptionCallback(asMETHOD(Script, ExceptionCallback), this, asCALL_THISCALL);
-    
+
     // Register the Array & String types
     RegisterArray(scriptEngine_);
     RegisterString(scriptEngine_);
@@ -168,10 +170,10 @@ Script::~Script()
         immediateContext_->Release();
         immediateContext_ = 0;
     }
-    
+
     for (unsigned i = 0 ; i < scriptFileContexts_.Size(); ++i)
         scriptFileContexts_[i]->Release();
-    
+
     if (scriptEngine_)
     {
         scriptEngine_->Release();
@@ -183,11 +185,11 @@ bool Script::Execute(const String& line)
 {
     // Note: compiling code each time is slow. Not to be used for performance-critical or repeating activity
     PROFILE(ExecuteImmediate);
-    
+
     ClearObjectTypeCache();
-    
+
     String wrappedLine = "void f(){\n" + line + ";\n}";
-    
+
     // If no immediate mode script file set, create a dummy module for compiling the line
     asIScriptModule* module = 0;
     if (defaultScriptFile_)
@@ -196,21 +198,21 @@ bool Script::Execute(const String& line)
         module = scriptEngine_->GetModule("ExecuteImmediate", asGM_CREATE_IF_NOT_EXISTS);
     if (!module)
         return false;
-    
+
     asIScriptFunction *function = 0;
     if (module->CompileFunction("", wrappedLine.CString(), -1, 0, &function) < 0)
         return false;
-    
+
     if (immediateContext_->Prepare(function) < 0)
     {
         function->Release();
         return false;
     }
-    
+
     bool success = immediateContext_->Execute() >= 0;
     immediateContext_->Unprepare();
     function->Release();
-    
+
     return success;
 }
 
@@ -238,35 +240,35 @@ void Script::DumpAPI()
 {
     // Does not use LOGRAW macro here to ensure the messages are always dumped regarless of ENABLE_LOGGING compiler directive and of Log subsystem availability
     Log::WriteRaw("namespace Urho3D\n{\n\n/**\n\\page ScriptAPI Scripting API\n\n");
-    
+
     Vector<PropertyInfo> globalPropertyInfos;
     Vector<String> globalFunctions;
-    
+
     unsigned functions = scriptEngine_->GetGlobalFunctionCount();
     for (unsigned i = 0; i < functions; ++i)
     {
         asIScriptFunction* function = scriptEngine_->GetGlobalFunctionByIndex(i);
         String functionName(function->GetName());
         String declaration(function->GetDeclaration());
-        
+
         if (functionName.Contains("set_") || functionName.Contains("get_"))
             ExtractPropertyInfo(functionName, declaration, globalPropertyInfos);
         else
             globalFunctions.Push(declaration);
     }
-    
+
     Log::WriteRaw("\\section ScriptAPI_GlobalFunctions Global functions\n");
-    
+
     for (unsigned i = 0; i < globalFunctions.Size(); ++i)
         OutputAPIRow(globalFunctions[i]);
-    
+
     Log::WriteRaw("\\section ScriptAPI_GlobalProperties Global properties\n");
-    
+
     for (unsigned i = 0; i < globalPropertyInfos.Size(); ++i)
         OutputAPIRow(globalPropertyInfos[i].type_ + " " + globalPropertyInfos[i].name_, true);
-    
+
     Log::WriteRaw("\\section ScriptAPI_GlobalConstants Global constants\n");
-    
+
     unsigned properties = scriptEngine_->GetGlobalPropertyCount();
     for (unsigned i = 0; i < properties; ++i)
     {
@@ -275,13 +277,13 @@ void Script::DumpAPI()
         int typeId;
         scriptEngine_->GetGlobalPropertyByIndex(i, &propertyName, 0, &typeId);
         propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-        
+
         String type(propertyDeclaration);
         OutputAPIRow(type + " " + String(propertyName), true);
     }
-    
+
     Log::WriteRaw("\\section ScriptAPI_Classes Classes\n");
-    
+
     unsigned types = scriptEngine_->GetObjectTypeCount();
     for (unsigned i = 0; i < types; ++i)
     {
@@ -291,9 +293,9 @@ void Script::DumpAPI()
             String typeName(type->GetName());
             Vector<String> methodDeclarations;
             Vector<PropertyInfo> propertyInfos;
-            
+
             Log::WriteRaw("\n" + typeName + "\n");
-            
+
             unsigned methods = type->GetMethodCount();
             for (unsigned j = 0; j < methods; ++j)
             {
@@ -313,7 +315,7 @@ void Script::DumpAPI()
                     }
                 }
             }
-            
+
             // Assume that the same property is never both an accessor property, and a direct one
             unsigned properties = type->GetPropertyCount();
             for (unsigned j = 0; j < properties; ++j)
@@ -321,24 +323,24 @@ void Script::DumpAPI()
                 const char* propertyName;
                 const char* propertyDeclaration;
                 int typeId;
-                
+
                 type->GetProperty(j, &propertyName, &typeId);
                 propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-                
+
                 PropertyInfo newInfo;
                 newInfo.name_ = String(propertyName);
                 newInfo.type_ = String(propertyDeclaration);
                 newInfo.read_ = newInfo.write_ = true;
                 propertyInfos.Push(newInfo);
             }
-            
+
             if (!methodDeclarations.Empty())
             {
                 Log::WriteRaw("\nMethods:<br>\n");
                 for (unsigned j = 0; j < methodDeclarations.Size(); ++j)
                     OutputAPIRow(methodDeclarations[j]);
             }
-            
+
             if (!propertyInfos.Empty())
             {
                 Log::WriteRaw("\nProperties:<br>\n");
@@ -349,15 +351,15 @@ void Script::DumpAPI()
                         remark = " (readonly)";
                     else if (!propertyInfos[j].read_)
                         remark = " (writeonly)";
-                    
+
                     OutputAPIRow(propertyInfos[j].type_ + " " + propertyInfos[j].name_ + remark);
                 }
             }
-            
+
             Log::WriteRaw("\n");
         }
     }
-    
+
     Log::WriteRaw("*/\n\n}\n");
 }
 
@@ -365,7 +367,7 @@ void Script::MessageCallback(const asSMessageInfo* msg)
 {
     String message;
     message.AppendWithFormat("%s:%d,%d %s", msg->section, msg->row, msg->col, msg->message);
-    
+
     if (logMode_ == LOGMODE_IMMEDIATE)
     {
         switch (msg->type)
@@ -373,11 +375,11 @@ void Script::MessageCallback(const asSMessageInfo* msg)
         case asMSGTYPE_ERROR:
             LOGERROR(message);
             break;
-            
+
         case asMSGTYPE_WARNING:
             LOGWARNING(message);
             break;
-            
+
         default:
             LOGINFO(message);
             break;
@@ -395,12 +397,12 @@ void Script::ExceptionCallback(asIScriptContext* context)
 {
     String message;
     message.AppendWithFormat("- Exception '%s' in '%s'\n%s", context->GetExceptionString(), context->GetExceptionFunction()->GetDeclaration(), GetCallStack(context).CString());
-    
+
     asSMessageInfo msg;
     msg.row = context->GetExceptionLineNumber(&msg.col, &msg.section);
     msg.type = asMSGTYPE_ERROR;
     msg.message = message.CString();
-    
+
     MessageCallback(&msg);
 }
 
@@ -442,7 +444,7 @@ asIObjectType* Script::GetObjectType(const char* declaration)
     HashMap<const char*, asIObjectType*>::ConstIterator i = objectTypes_.Find(declaration);
     if (i != objectTypes_.End())
         return i->second_;
-    
+
     asIObjectType* type = scriptEngine_->GetObjectTypeById(scriptEngine_->GetTypeIdByDecl(declaration));
     objectTypes_[declaration] = type;
     return type;
@@ -456,7 +458,7 @@ asIScriptContext* Script::GetScriptFileContext()
         newContext->SetExceptionCallback(asMETHOD(Script, ExceptionCallback), this, asCALL_THISCALL);
         scriptFileContexts_.Push(newContext);
     }
-    
+
     return scriptFileContexts_[scriptNestingLevel_];
 }
 
@@ -471,7 +473,7 @@ void Script::OutputAPIRow(const String& row, bool removeReference)
     out.Replace("&out", "&");
     if (removeReference)
         out.Replace("&", "");
-    
+
     Log::WriteRaw("- " + out + "\n");
 }
 

+ 8 - 6
Engine/Script/Script.h

@@ -34,6 +34,8 @@ struct asSMessageInfo;
 namespace Urho3D
 {
 
+extern const char* SCRIPT_CATEGORY;
+
 class Scene;
 class ScriptFile;
 class ScriptInstance;
@@ -49,15 +51,15 @@ enum ScriptLogMode
 class Script : public Object
 {
     OBJECT(Script);
-    
+
     friend class ScriptFile;
-    
+
 public:
     /// Construct.
     Script(Context* context);
     /// Destruct. Release the AngelScript engine.
     ~Script();
-    
+
     /// Compile and execute a line of script in immediate mode.
     bool Execute(const String& line);
     /// Set immediate mode script file.
@@ -76,7 +78,7 @@ public:
     void ExceptionCallback(asIScriptContext* context);
     /// Get call stack.
     static String GetCallStack(asIScriptContext* context);
-    
+
     /// Return the AngelScript engine.
     asIScriptEngine* GetScriptEngine() const { return scriptEngine_; }
     /// Return immediate execution script context.
@@ -93,7 +95,7 @@ public:
     ScriptLogMode GetLogMode() const { return logMode_; }
     /// Return retained mode log messages.
     const String& GetLogMessages() const { return logMessages_; }
-    
+
 private:
     /// Increase script nesting level.
     void IncScriptNestingLevel() { ++scriptNestingLevel_; }
@@ -105,7 +107,7 @@ private:
     asIScriptContext* GetScriptFileContext();
     /// Output a sanitated row of script API. No-ops when ENABLE_LOGGING not defined.
     void OutputAPIRow(const String& row, bool removeReference = false);
-    
+
     /// AngelScript engine.
     asIScriptEngine* scriptEngine_;
     /// Immediate execution script context.

+ 1 - 1
Engine/Script/ScriptInstance.cpp

@@ -81,7 +81,7 @@ ScriptInstance::~ScriptInstance()
 
 void ScriptInstance::RegisterObject(Context* context)
 {
-    context->RegisterFactory<ScriptInstance>();
+    context->RegisterComponentFactory<ScriptInstance>(SCRIPT_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(ScriptFile::GetTypeStatic()), AM_DEFAULT);

+ 3 - 1
Engine/UI/Text3D.cpp

@@ -37,6 +37,8 @@
 namespace Urho3D
 {
 
+static const char* EFFECT_CATEGORY = "Effect";
+
 extern const char* horizontalAlignments[];
 extern const char* verticalAlignments[];
 
@@ -61,7 +63,7 @@ Text3D::~Text3D()
 
 void Text3D::RegisterObject(Context* context)
 {
-    context->RegisterFactory<Text3D>();
+    context->RegisterComponentFactory<Text3D>(EFFECT_CATEGORY);
     
     ACCESSOR_ATTRIBUTE(Text3D, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Text3D, VAR_RESOURCEREF, "Font", GetFontAttr, SetFontAttr, ResourceRef, ResourceRef(Font::GetTypeStatic()), AM_DEFAULT);