[Gtkradiant] r4792 - in GtkRadiant/trunk: docs/developer include libs libs/math plugins/entity plugins/md3model radiant

svn-noreply at zerowing.idsoftware.com svn-noreply at zerowing.idsoftware.com
Wed Sep 22 05:21:29 CDT 2004


Author: spog
Date: 2004-09-21 13:46:46 -0500 (Tue, 21 Sep 2004)
New Revision: 4792

Added:
   GtkRadiant/trunk/libs/math/curve.cpp
   GtkRadiant/trunk/libs/math/curve.h
Modified:
   GtkRadiant/trunk/docs/developer/CHANGES
   GtkRadiant/trunk/docs/developer/TODO
   GtkRadiant/trunk/include/namespace.h
   GtkRadiant/trunk/libs/eclasslib.h
   GtkRadiant/trunk/libs/entitylib.h
   GtkRadiant/trunk/libs/libs.vcproj
   GtkRadiant/trunk/libs/math/frustum.h
   GtkRadiant/trunk/libs/math/vector.h
   GtkRadiant/trunk/libs/scenelib.h
   GtkRadiant/trunk/libs/stringio.h
   GtkRadiant/trunk/libs/traverselib.h
   GtkRadiant/trunk/libs/undolib.h
   GtkRadiant/trunk/plugins/entity/entity.cpp
   GtkRadiant/trunk/plugins/entity/light.cpp
   GtkRadiant/trunk/plugins/entity/namekeys.h
   GtkRadiant/trunk/plugins/entity/static.cpp
   GtkRadiant/trunk/plugins/entity/targetable.cpp
   GtkRadiant/trunk/plugins/entity/targetable.h
   GtkRadiant/trunk/plugins/md3model/md5.cpp
   GtkRadiant/trunk/radiant/brush.h
   GtkRadiant/trunk/radiant/eclass_doom3.cpp
   GtkRadiant/trunk/radiant/entityinspector.cpp
   GtkRadiant/trunk/radiant/mainframe.cpp
   GtkRadiant/trunk/radiant/map.cpp
   GtkRadiant/trunk/radiant/referencecache.cpp
   GtkRadiant/trunk/radiant/texwindow.cpp
   GtkRadiant/trunk/radiant/winding.cpp
   GtkRadiant/trunk/radiant/winding.h
Log:
full doom3 'targetN' support; bug fixes; doom3 spline rendering

Modified: GtkRadiant/trunk/docs/developer/CHANGES
===================================================================
--- GtkRadiant/trunk/docs/developer/CHANGES	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/docs/developer/CHANGES	2004-09-21 18:46:46 UTC (rev 4792)
@@ -1,6 +1,31 @@
 This is the changelog for developers, != changelog for the end user 
 that we distribute with the binaries. (see changelog)
 
+20/09/2004
+SPoG
+- Added basic rendering for 'curve_CatmullRomSpline' on doom3 entities.
+
+19/09/2004
+SPoG
+- Added basic rendering for 'curve_Nurbs' on doom3 entities.
+
+18/09/2004
+SPoG
+- Added doom3 entity-definition editor_usage* key support.
+- Added doom3 entity-definition editor_* key info to entity-inspector comments.
+- Added specialised attribute-entry in entity-inspector for boolean attributes.
+- Fixed crash in find-brush when entering the index of a fixed-size entity.
+- Added support for rendering doom3 'targetN' -> 'name' entity connections.
+- Changed connect-entities to use 'targetN' and 'name' keys on doom3 entities.
+- Improved handling of brushes with near-duplicate planes.
+- Fixed crash in snap-to-grid on brushes with non-contributing faces.
+
+17/09/2004
+Michael Schlueter
+- Fixed gcc build errors.
+SPoG
+- Fixed stability problems with doom3 brush-entities.
+
 16/09/2004
 SPoG
 - Added automatic renaming of entity target/name keys for paste and import-map.

Modified: GtkRadiant/trunk/docs/developer/TODO
===================================================================
--- GtkRadiant/trunk/docs/developer/TODO	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/docs/developer/TODO	2004-09-21 18:46:46 UTC (rev 4792)
@@ -17,23 +17,20 @@
 
 HIGH priority features
 
-Patch: add cap-texture fit-texture and natural-texture toolbar buttons
-Entity: support doom3 nurbs curves
+Brush: make edge/vertex/face dragging with QE tool select equal points
 UI: change file-choosers to use GtkFileChooser on linux
-Brush: warn when a brush is dragged into a configuration with <0 volume
-Model: support doom3 .skin files
-Entity: add mouse-resizing for doom3 light_radius key
 Model: add support for doom3 md5anim format
-HalfLife: disable patches
+Model: support doom3 .skin files
 VFS: add ability to browse VFS from file-open dialogs.
-Entity: add specialised attribute-entry in entity-inspector for boolean/color/shader/list attribute types.
 Installer: separate example-maps from main editor in linux installer.
 Installer: enable q3 brush-primitives map support.
+Installer: add editor manual to linux installer
 Textures: remove "shaders only" option for doom3
-Documentation: add manuals to installer
 Map: support import/conversion of other map formats to current-game format
 Map: create worldspawn first for .map format.
 Brush: add texture painting feature
+Brush: warn when a brush is dragged into a configuration with <0 volume
+HalfLife: disable patches
 HalfLife: add HL .mdl model loader.
 HalfLife: add HL .spr support.
 HalfLife: add support for Hammer texture-matrix.
@@ -41,25 +38,27 @@
 Renderer: realtime doom3 lighting preview
 Renderer: realtime doom3 shadows preview
 Build System: add build-menu dmap support (doom3)
-Entity: support doom3 target* key
+Entity: support doom3 nurbs curves
+Entity: add mouse-resizing for doom3 light_radius key
+Entity: add specialised attribute-entry in entity-inspector for color/shader/list attribute types.
 Entity: support doom3 light_radius key
-Entity: doom3 \entity key descriptions in 'n' window
 Entity: light modification window ('j' in doomedit)
-Entity: entity names - generate new names for cloned/pasted entities
 Entity: add rotate-tool support for 'angle' key
 Entity: add 'angle' ui stuff to entity inspector
-Patch: support patchDef3 S/T tesselation
 svn: remove install/ dir, create it during build process on win32
 Editing: add resizing of patches with QE tool
+Patch: add cap-texture, fit-texture and natural-texture toolbar buttons
+Patch: support patchDef3 S/T tesselation
 Patch: draw patches in wireframe from the back, make patches selectable from the back
 Patch: add option for create-from-selection
+Patch: add merge-patches feature
 Selection: convert-selection-to-brushes for entity selections
 Selection: distinguish between entity and brush selections
 Selection: 'add to selection' and 'subtract from selection' modifiers
 Shortcuts: warn when duplicate shortcuts are registered
 Selection: speed up 'invert selection' on large maps
-UI: migrate to GtkFileChooser for directory/file chooser dialogs
 Selection: Finish scale manipulator (also, rescale/regenerate normals and re-tesselate patches).
+UI: migrate to GtkFileChooser for directory/file chooser dialogs
 Clipper: Indicate which side is front and which is back.
 Autosave/Snapshots: Add support for multiple files.
 Directories: Prompt for engine path at startup if not set.

Modified: GtkRadiant/trunk/include/namespace.h
===================================================================
--- GtkRadiant/trunk/include/namespace.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/include/namespace.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -16,6 +16,7 @@
   }
   virtual void attach(const NameClosure& setName, const NameClosureClosure& attachObserver) = 0;
   virtual void detach(const NameClosure& setName, const NameClosureClosure& detachObserver) = 0;
+  virtual void makeUnique(const char* name, const NameClosure& setName) const = 0;
 };
 
 class Namespaced

Modified: GtkRadiant/trunk/libs/eclasslib.h
===================================================================
--- GtkRadiant/trunk/libs/eclasslib.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/eclasslib.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -20,7 +20,8 @@
   string_t m_type;
   string_t m_key;
   string_t m_name;
-  EntityClassAttribute(const char* type, const char* key, const char* name) : m_type(type), m_key(key), m_name(name)
+  string_t m_description;
+  EntityClassAttribute(const char* type, const char* key, const char* name, const char* description = "") : m_type(type), m_key(key), m_name(name), m_description(description)
   {
   }
 };

Modified: GtkRadiant/trunk/libs/entitylib.h
===================================================================
--- GtkRadiant/trunk/libs/entitylib.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/entitylib.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -246,7 +246,7 @@
 
 typedef Closure1<const char*> KeyObserver;
 
-class KeyValue : public UndoableObjectObserver
+class KeyValue
 {
   typedef UnsortedSet<KeyObserver> KeyObservers;
 
@@ -258,7 +258,7 @@
 public:
 
   KeyValue(const char* string)
-    : m_refcount(0), m_string(string), m_undo(m_string, *this)
+    : m_refcount(0), m_string(string), m_undo(m_string, UndoImportCaller(*this))
   {
     notify();
   }
@@ -326,13 +326,16 @@
     }
   }
 
-  void postUndoImport()
+  void undoImport(const string_t& string)
   {
+    m_string = string;
+
     notify();
   }
+  typedef MemberCaller1<KeyValue, const string_t&>::Caller<&KeyValue::undoImport> UndoImportCaller;
 };
 
-class EntityKeyValues : public Entity, public UndoableObjectObserver
+class EntityKeyValues : public Entity
 {
 public:
   typedef KeyValue Value;
@@ -348,10 +351,11 @@
   static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
   static Counter* m_counter;
 
-  typedef UnsortedMap<string_t, refcounted_ptr<KeyValue> > KeyValues;
-  KeyValues m_keyvalues;
+  typedef refcounted_ptr<KeyValue> KeyValuePtr;
+  typedef UnsortedMap<string_t, KeyValuePtr > KeyValues;
+  KeyValues m_keyValues;
 
-  typedef std::set<Observer*> Observers;
+  typedef UnsortedSet<Observer*> Observers;
   Observers m_observers;
 
   ObservedUndoableObject<KeyValues> m_undo;
@@ -359,66 +363,104 @@
 
   EntityClass* m_eclass;
 
-  void notify_insert(const char* key, Value& value)
+  bool m_observerMutex;
+
+  void notifyInsert(const char* key, Value& value)
   {
+    m_observerMutex = true;
     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
     {
       (*i)->insert(key, value);
     }
+    m_observerMutex = false;
   }
-  void notify_erase(const char* key, Value& value)
+  void notifyErase(const char* key, Value& value)
   {
+    m_observerMutex = true;
     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
     {
       (*i)->erase(key, value);
     }
+    m_observerMutex = false;
   }
-  void notify_insert_all()
+  void forEachKeyValue_notifyInsert()
   {
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
-      notify_insert((*i).first.c_str(), *(*i).second);
+      notifyInsert((*i).first.c_str(), *(*i).second);
     }
   }
-  void notify_erase_all()
+  void forEachKeyValue_notifyErase()
   {
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
-      notify_erase((*i).first.c_str(), *(*i).second);
+      notifyErase((*i).first.c_str(), *(*i).second);
     }
   }
 
-  void insert(const char* key, const char* value)
+  void insert(const char* key, const KeyValuePtr& keyValue)
   {
-    m_undo.save();
-    Value* keyValue = new KeyValue(value);
-    notify_insert(key, *keyValue);
-    KeyValues::iterator i = m_keyvalues.insert(KeyValues::value_type(key, keyValue));
+    KeyValues::iterator i = m_keyValues.insert(KeyValues::value_type(key, keyValue));
+    notifyInsert(key, *(*i).second);
+
     if(m_instanced)
     {
       (*i).second->instanceAttach(m_undo.map());
     }
-    m_entityKeyValueChanged();
   }
+
+  void insert(const char* key, const char* value)
+  {
+    KeyValues::iterator i = m_keyValues.find(key);
+    if(i != m_keyValues.end())
+    {
+      (*i).second->assign(value);
+    }
+    else
+    {
+      m_undo.save();
+      insert(key, new KeyValue(value));
+    }
+  }
+
   void erase(KeyValues::iterator i)
   {
-    m_undo.save();
     if(m_instanced)
     {
       (*i).second->instanceDetach(m_undo.map());
     }
-    notify_erase((*i).first.c_str(), *(*i).second);
-    m_keyvalues.erase(i);
-    m_entityKeyValueChanged();
+
+    string_t key((*i).first);
+    KeyValuePtr value((*i).second);
+    m_keyValues.erase(i);
+    notifyErase(key.c_str(), *value);
   }
 
+  void erase(const char* key)
+  {
+    KeyValues::iterator i = m_keyValues.find(key);
+    if(i != m_keyValues.end())
+    {
+      m_undo.save();
+      erase(i);
+    }
+  }
+
 public:
-  EntityKeyValues(EntityClass* eclass) : m_eclass(eclass), m_undo(m_keyvalues, *this), m_instanced(false)
+  EntityKeyValues(EntityClass* eclass) :
+    m_eclass(eclass),
+    m_undo(m_keyValues, UndoImportCaller(*this)),
+    m_instanced(false),
+    m_observerMutex(false)
   {
   }
-  EntityKeyValues(const EntityKeyValues& other) : m_eclass(&other.eclass()), m_undo(m_keyvalues, *this), m_instanced(false)
+  EntityKeyValues(const EntityKeyValues& other) :
+    m_eclass(&other.eclass()),
+    m_undo(m_keyValues, UndoImportCaller(*this)),
+    m_instanced(false),
+    m_observerMutex(false)
   {
-    for(KeyValues::const_iterator i = other.m_keyvalues.begin(); i != other.m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = other.m_keyValues.begin(); i != other.m_keyValues.end(); ++i)
     {
       insert((*i).first.c_str(), (*i).second->c_str());
     }
@@ -434,38 +476,38 @@
     m_counter = counter;
   }
 
-  void preUndoImport()
+  void undoImport(const KeyValues& keyValues)
   {
-    notify_erase_all();
-    if(m_instanced)
+    for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end();)
     {
-      forEachKeyValue_instanceDetach(m_undo.map());
+      erase(i++);
     }
-  }
-  void postUndoImport()
-  {
-    if(m_instanced)
+
+    for(KeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i)
     {
-      forEachKeyValue_instanceAttach(m_undo.map());
+      insert((*i).first.c_str(), (*i).second);
     }
-    notify_insert_all();
+
     m_entityKeyValueChanged();
   }
+  typedef MemberCaller1<EntityKeyValues, const KeyValues&>::Caller<&EntityKeyValues::undoImport> UndoImportCaller;
 
   void attach(Observer& observer)
   {
+    RADIANT_ASSERT(!m_observerMutex, "observer cannot be attached during iteration");
     RADIANT_ASSERT(m_observers.find(&observer) == m_observers.end(), "observer cannot be attached");
     m_observers.insert(&observer);
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
       observer.insert((*i).first.c_str(), *(*i).second);
     }
   }
   void detach(Observer& observer)
   {
+    RADIANT_ASSERT(!m_observerMutex, "observer cannot be detached during iteration");
     RADIANT_ASSERT(m_observers.find(&observer) != m_observers.end(), "observer cannot be detached");
     m_observers.erase(&observer);
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
       observer.erase((*i).first.c_str(), *(*i).second);
     }
@@ -473,14 +515,14 @@
 
   void forEachKeyValue_instanceAttach(MapFile* map)
   {
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
       (*i).second->instanceAttach(map);
     }
   }
   void forEachKeyValue_instanceDetach(MapFile* map)
   {
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
       (*i).second->instanceDetach(map);
     }
@@ -516,7 +558,7 @@
   }
   void accept(Visitor& visitor) const
   {
-    for(KeyValues::const_iterator i = m_keyvalues.begin(); i != m_keyvalues.end(); ++i)
+    for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
     {
       visitor.visit((*i).first.c_str(), (*i).second->c_str());
     }
@@ -525,29 +567,18 @@
   {
     if(value[0] == '\0')
     {
-      KeyValues::iterator i = m_keyvalues.find(key);
-      if(i != m_keyvalues.end())
-      {
-        erase(i);
-      }
+      erase(key);
     }
     else
     {
-      KeyValues::iterator i = m_keyvalues.find(key);
-      if(i != m_keyvalues.end())
-      {
-        (*i).second->assign(value);
-      }
-      else
-      {
-        insert(key, value);
-      }
+      insert(key, value);
     }
+    m_entityKeyValueChanged();
   }
   const char* valueforkey(const char* key) const
   {
-    KeyValues::const_iterator i = m_keyvalues.find(key);
-    if(i != m_keyvalues.end())
+    KeyValues::const_iterator i = m_keyValues.find(key);
+    if(i != m_keyValues.end())
     {
       return (*i).second->c_str();
     }

Modified: GtkRadiant/trunk/libs/libs.vcproj
===================================================================
--- GtkRadiant/trunk/libs/libs.vcproj	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/libs.vcproj	2004-09-21 18:46:46 UTC (rev 4792)
@@ -133,6 +133,12 @@
 				RelativePath=".\math\matrix.h">
 			</File>
 			<File
+				RelativePath=".\math\nurbscurve.cpp">
+			</File>
+			<File
+				RelativePath=".\math\nurbscurve.h">
+			</File>
+			<File
 				RelativePath=".\math\pi.cpp">
 			</File>
 			<File

Added: GtkRadiant/trunk/libs/math/curve.cpp
===================================================================
--- GtkRadiant/trunk/libs/math/curve.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/math/curve.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -0,0 +1,3 @@
+
+#include "curve.h"
+


Property changes on: GtkRadiant/trunk/libs/math/curve.cpp
___________________________________________________________________
Name: svn:eol-style
   + native

Added: GtkRadiant/trunk/libs/math/curve.h
===================================================================
--- GtkRadiant/trunk/libs/math/curve.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/math/curve.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -0,0 +1,5 @@
+
+#if !defined(_INCLUDED_CURVE_H_)
+#define _INCLUDED_CURVE_H_
+
+#endif


Property changes on: GtkRadiant/trunk/libs/math/curve.h
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: GtkRadiant/trunk/libs/math/frustum.h
===================================================================
--- GtkRadiant/trunk/libs/math/frustum.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/math/frustum.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -83,7 +83,7 @@
       bool b1 = ClipPlane::compare(*next);
       if(b0 ^ b1)
       {
-        *out = vector4_subtract(*next, *i);
+        *out = vector4_subtracted(*next, *i);
 
         double scale = ClipPlane::scale(*i, *out);
 
@@ -197,7 +197,7 @@
     const bool index = CLIP_X_LT_W(p0);
     if(index ^ CLIP_X_LT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[0] - p0[3]) / (clip[3] - clip[0]);
 
@@ -216,7 +216,7 @@
     const bool index = CLIP_X_GT_W(p0);
     if(index ^ CLIP_X_GT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[0] + p0[3]) / (-clip[3] - clip[0]);
 
@@ -235,7 +235,7 @@
     const bool index = CLIP_Y_LT_W(p0);
     if(index ^ CLIP_Y_LT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[1] - p0[3]) / (clip[3] - clip[1]);
 
@@ -254,7 +254,7 @@
     const bool index = CLIP_Y_GT_W(p0);
     if(index ^ CLIP_Y_GT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[1] + p0[3]) / (-clip[3] - clip[1]);
 
@@ -273,7 +273,7 @@
     const bool index = CLIP_Z_LT_W(p0);
     if(index ^ CLIP_Z_LT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[2] - p0[3]) / (clip[3] - clip[2]);
 
@@ -292,7 +292,7 @@
     const bool index = CLIP_Z_GT_W(p0);
     if(index ^ CLIP_Z_GT_W(p1))
     {
-      Vector4 clip(vector4_subtract(p1, p0));
+      Vector4 clip(vector4_subtracted(p1, p0));
 
       double scale = (p0[2] + p0[3]) / (-clip[3] - clip[2]);
 

Modified: GtkRadiant/trunk/libs/math/vector.h
===================================================================
--- GtkRadiant/trunk/libs/math/vector.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/math/vector.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -383,6 +383,33 @@
 }
 
 template<typename Element, typename OtherElement>
+inline BasicVector3<Element> vector3_divided(const BasicVector3<Element>& self, const OtherElement& divisor)
+{
+  return BasicVector3<Element>(
+    Element(self.x() / divisor),
+    Element(self.y() / divisor),
+    Element(self.z() / divisor)
+  );
+}
+template<typename Element, typename OtherElement>
+inline BasicVector3<Element> operator/(const BasicVector3<Element>& self, const OtherElement& divisor)
+{
+  return vector3_divided(self, divisor);
+}
+template<typename Element, typename OtherElement>
+inline void vector3_divide(BasicVector3<Element>& self, const OtherElement& divisor)
+{
+  self.x() /= static_cast<Element>(divisor);
+  self.y() /= static_cast<Element>(divisor);
+  self.z() /= static_cast<Element>(divisor);
+}
+template<typename Element, typename OtherElement>
+inline void operator/=(BasicVector3<Element>& self, const OtherElement& divisor)
+{
+  vector3_divide(self, divisor);
+}
+
+template<typename Element, typename OtherElement>
 inline double vector3_dot(const BasicVector3<Element>& self, const BasicVector3<OtherElement>& other)
 {
   return self.x() * other.x() + self.y() * other.y() + self.z() * other.z();
@@ -586,9 +613,129 @@
   return reinterpret_cast<const Vector3&>(self);
 }
 
-inline Vector4 vector4_subtract(const Vector4& self, const Vector4& other)
+inline Vector4 vector4_added(const Vector4& self, const Vector4& other)
 {
-  return Vector4(self.x() - other.x(), self.y() - other.y(), self.z() - other.z(), self.w() - other.w());
+  return Vector4(
+    float(self.x() + other.x()),
+    float(self.y() + other.y()),
+    float(self.z() + other.z()),
+    float(self.w() + other.w())
+  );
 }
+inline Vector4 operator+(const Vector4& self, const Vector4& other)
+{
+  return vector4_added(self, other);
+}
+inline void vector4_add(Vector4& self, const Vector4& other)
+{
+  self.x() += static_cast<float>(other.x());
+  self.y() += static_cast<float>(other.y());
+  self.z() += static_cast<float>(other.z());
+  self.w() += static_cast<float>(other.w());
+}
+inline void operator+=(Vector4& self, const Vector4& other)
+{
+  vector4_add(self, other);
+}
 
+inline Vector4 vector4_subtracted(const Vector4& self, const Vector4& other)
+{
+  return Vector4(
+    float(self.x() - other.x()),
+    float(self.y() - other.y()),
+    float(self.z() - other.z()),
+    float(self.w() - other.w())
+  );
+}
+inline Vector4 operator-(const Vector4& self, const Vector4& other)
+{
+  return vector4_subtracted(self, other);
+}
+inline void vector4_subtract(Vector4& self, const Vector4& other)
+{
+  self.x() -= static_cast<float>(other.x());
+  self.y() -= static_cast<float>(other.y());
+  self.z() -= static_cast<float>(other.z());
+  self.w() -= static_cast<float>(other.w());
+}
+inline void operator-=(Vector4& self, const Vector4& other)
+{
+  vector4_subtract(self, other);
+}
+
+inline Vector4 vector4_scaled(const Vector4& self, const Vector4& other)
+{
+  return Vector4(
+    float(self.x() * other.x()),
+    float(self.y() * other.y()),
+    float(self.z() * other.z()),
+    float(self.w() * other.w())
+  );
+}
+inline Vector4 operator*(const Vector4& self, const Vector4& other)
+{
+  return vector4_scaled(self, other);
+}
+inline void vector4_scale(Vector4& self, const Vector4& other)
+{
+  self.x() *= static_cast<float>(other.x());
+  self.y() *= static_cast<float>(other.y());
+  self.z() *= static_cast<float>(other.z());
+  self.w() *= static_cast<float>(other.w());
+}
+inline void operator*=(Vector4& self, const Vector4& other)
+{
+  vector4_scale(self, other);
+}
+
+inline Vector4 vector4_scaled(const Vector4& self, const float& scale)
+{
+  return Vector4(
+    float(self.x() * scale),
+    float(self.y() * scale),
+    float(self.z() * scale),
+    float(self.w() * scale)
+  );
+}
+inline Vector4 operator*(const Vector4& self, const float& scale)
+{
+  return vector4_scaled(self, scale);
+}
+inline void vector4_scale(Vector4& self, const float& scale)
+{
+  self.x() *= static_cast<float>(scale);
+  self.y() *= static_cast<float>(scale);
+  self.z() *= static_cast<float>(scale);
+  self.w() *= static_cast<float>(scale);
+}
+inline void operator*=(Vector4& self, const float& scale)
+{
+  vector4_scale(self, scale);
+}
+
+inline Vector4 vector4_divided(const Vector4& self, const float& divisor)
+{
+  return Vector4(
+    float(self.x() / divisor),
+    float(self.y() / divisor),
+    float(self.z() / divisor),
+    float(self.w() / divisor)
+  );
+}
+inline Vector4 operator/(const Vector4& self, const float& divisor)
+{
+  return vector4_divided(self, divisor);
+}
+inline void vector4_divide(Vector4& self, const float& divisor)
+{
+  self.x() /= static_cast<float>(divisor);
+  self.y() /= static_cast<float>(divisor);
+  self.z() /= static_cast<float>(divisor);
+  self.w() /= static_cast<float>(divisor);
+}
+inline void operator/=(Vector4& self, const float& divisor)
+{
+  vector4_divide(self, divisor);
+}
+
 #endif

Modified: GtkRadiant/trunk/libs/scenelib.h
===================================================================
--- GtkRadiant/trunk/libs/scenelib.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/scenelib.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -35,7 +35,8 @@
     m_data(0),
     m_end(0),
     m_capacity(0)
-  {}
+  {
+  }
   Stack(const value_type n) :
     m_data(0),
     m_capacity(0),

Modified: GtkRadiant/trunk/libs/stringio.h
===================================================================
--- GtkRadiant/trunk/libs/stringio.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/stringio.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -7,11 +7,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-inline bool string_read_vector(const char* string, Vector3& vector3)
-{
-  return sscanf(string, "%f %f %f", &vector3[0], &vector3[1], &vector3[2]) == 3;
-}
-
 inline float string_read_float(const char* string)
 {
   return static_cast<float>(atof(string));
@@ -22,20 +17,49 @@
   return atoi(string);
 }
 
+inline bool string_parse_float(const char* string, float& f)
+{
+  return sscanf(string, "%f", &f) == 1;
+}
+
+inline bool string_parse_double(const char* string, double& f)
+{
+  return sscanf(string, "%lf", &f) == 1;
+}
+
+inline bool string_parse_vector(const char* string, Vector3& vector3)
+{
+  return sscanf(string, "%f %f %f", &vector3[0], &vector3[1], &vector3[2]) == 3;
+}
+
+inline bool string_parse_int(const char* string, int& i)
+{
+  return sscanf(string, "%i", &i) == 1;
+}
+
+inline bool string_parse_size(const char* string, std::size_t& i)
+{
+  return sscanf(string, "%u", &i) == 1;
+}
+
 inline bool Tokeniser_getFloat(Tokeniser* tokeniser, float& f)
 {
-  return sscanf(tokeniser->getToken(), "%f", &f) == 1;
+  return string_parse_float(tokeniser->getToken(), f);
 }
 
 inline bool Tokeniser_getDouble(Tokeniser* tokeniser, double& f)
 {
-  return sscanf(tokeniser->getToken(), "%lf", &f) == 1;
+  return string_parse_double(tokeniser->getToken(), f);
 }
 
 inline bool Tokeniser_getInteger(Tokeniser* tokeniser, int& i)
 {
-  return sscanf(tokeniser->getToken(), "%i", &i) == 1;
+  return string_parse_int(tokeniser->getToken(), i);
 }
 
+inline bool Tokeniser_getSize(Tokeniser* tokeniser, std::size_t& i)
+{
+  return string_parse_size(tokeniser->getToken(), i);
+}
 
 #endif

Modified: GtkRadiant/trunk/libs/traverselib.h
===================================================================
--- GtkRadiant/trunk/libs/traverselib.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/traverselib.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -114,7 +114,9 @@
   {
 #if 1 // optimised change-tracking using diff algorithm
     if(m_observer)
+    {
       nodeset_diff(m_children, other.m_children, m_observer);
+    }
     copy(other);
 #else
     ObservedNodeSet tmp(other);
@@ -147,14 +149,18 @@
     m_children.insert(n);
 
     if(m_observer)
+    {
       m_observer->insert(n);
+    }
   }
   void erase(scene::Node* n)
   {
     RADIANT_ASSERT(m_children.find(n) != m_children.end(), "TraversableSet::erase - failed to find element");
 
     if(m_observer)
+    {
       m_observer->erase(n);
+    }
 
     m_children.erase(n);
   }
@@ -165,7 +171,7 @@
     {
       // post-increment the iterator
       (*i++)->traverse(w);
-      // the Walker can safely remove the node from
+      // the Walker can safely remove the current node from
       // this container without invalidating the iterator
     }
   }
@@ -220,17 +226,27 @@
 class traverse_node : public scene::Traversable
 {
 public:
-  traverse_node()
-    : m_node(0), m_observer(0)
-  {}
+  traverse_node() : m_node(0), m_observer(0)
+  {
+  }
 
   // traverse
   void attach(Observer* observer)
   {
+    RADIANT_ASSERT(m_observer == 0, "traverse_node::attach - cannot attach observer");
     m_observer = observer;
+    if(m_node != 0)
+    {
+      m_observer->insert(m_node);
+    }
   }
   void detach(Observer* observer)
   {
+    RADIANT_ASSERT(m_observer == observer, "traverse_node::detach - cannot detach observer");
+    if(m_node != 0)
+    {
+      m_observer->erase(m_node);
+    }
     m_observer = 0;
   }
   void insert(scene::Node* n)
@@ -241,14 +257,18 @@
     n->IncRef();
 
     if(m_observer)
+    {
       m_observer->insert(n);
+    }
   }
   void erase(scene::Node* n)
   {
     RADIANT_ASSERT(m_node == n, "traverse_node::erase - failed to find element");
 
     if(m_observer)
+    {
       m_observer->erase(n);
+    }
 
     m_node = 0;
     n->DecRef();
@@ -256,7 +276,9 @@
   void traverse(scene::Traversable::Walker& w)
   {
     if(m_node)
+    {
       m_node->traverse(w);
+    }
   }
 
   scene::Node* get()

Modified: GtkRadiant/trunk/libs/undolib.h
===================================================================
--- GtkRadiant/trunk/libs/undolib.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/libs/undolib.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -5,14 +5,8 @@
 #include "iundo.h"
 #include "imap.h"
 #include "warnings.h"
+#include "functional.h"
 
-class UndoableObjectObserver
-{
-public:
-  virtual void preUndoImport() {};
-  virtual void postUndoImport() {};
-};
-
 template<typename Copyable>
 class BasicUndoData : public UndoData
 {
@@ -35,19 +29,21 @@
 };
 
 
-template <class object_type>
+template <typename Copyable>
 class ObservedUndoableObject : public Undoable
 {
-private:
-  object_type& m_object;
-  UndoableObjectObserver& m_observer;
+  typedef Closure1<const Copyable&> ImportClosure;
+
+  Copyable& m_object;
+  ImportClosure m_importClosure;
   UndoObserver* m_undoQueue;
   MapFile* m_map;
 public:
 
-  ObservedUndoableObject<object_type>(object_type& object, UndoableObjectObserver& observer)
-    : m_object(object), m_observer(observer), m_undoQueue(0), m_map(0)
-  {}
+  ObservedUndoableObject<Copyable>(Copyable& object, const ImportClosure& importClosure)
+    : m_object(object), m_importClosure(importClosure), m_undoQueue(0), m_map(0)
+  {
+  }
   ~ObservedUndoableObject()
   {
   }
@@ -83,26 +79,24 @@
 
   UndoData* undoExport() const
   {
-    return new BasicUndoData<object_type>(m_object);
+    return new BasicUndoData<Copyable>(m_object);
   }
   void undoImport(const UndoData* state)
   {
     save();
-    m_observer.preUndoImport();
-    m_object = (static_cast<const BasicUndoData<object_type>*>(state))->get();
-    m_observer.postUndoImport();
+    m_importClosure((static_cast<const BasicUndoData<Copyable>*>(state))->get());
   }
 };
 
-template <class object_type>
+template <class Copyable>
 class UndoableObject : public Undoable
 {
-  object_type& m_object;
+  Copyable& m_object;
   UndoObserver* m_undoQueue;
   MapFile* m_map;
 
 public:
-  UndoableObject(object_type& object)
+  UndoableObject(Copyable& object)
     : m_object(object), m_undoQueue(0), m_map(0)
   {}
   ~UndoableObject()
@@ -135,12 +129,12 @@
 
   UndoData* undoExport() const
   {
-    return new BasicUndoData<object_type>(m_object);
+    return new BasicUndoData<Copyable>(m_object);
   }
   void undoImport(const UndoData* state)
   {
     save();
-    m_object = (static_cast<const BasicUndoData<object_type>*>(state))->get();
+    m_object = (static_cast<const BasicUndoData<Copyable>*>(state))->get();
   }
 };
 

Modified: GtkRadiant/trunk/plugins/entity/entity.cpp
===================================================================
--- GtkRadiant/trunk/plugins/entity/entity.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/entity.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -12,6 +12,7 @@
 #include "targetable.h"
 #include "uniquenames.h"
 #include "namekeys.h"
+#include "stringstream.h"
 
 #if 0
 #include "registry.h"
@@ -129,6 +130,26 @@
 bool g_newLightDraw = true;
 bool g_lightRadii = false;
 
+class ConnectEntities
+{
+public:
+  const scene::Path& m_e1;
+  const scene::Path& m_e2;
+  ConnectEntities(const scene::Path& e1, const scene::Path& e2) : m_e1(e1), m_e2(e2)
+  {
+  }
+  void connect(const char* name)
+  {
+	  m_e1.top()->m_entity->setkeyvalue("target", name);
+	  m_e2.top()->m_entity->setkeyvalue("targetname", name);
+  }
+  typedef MemberCaller1<ConnectEntities, const char*>::Caller<&ConnectEntities::connect> ConnectCaller;
+};
+
+void EntitySetTargetTargetname(const char* name)
+{
+}
+
 class Quake3EntityCreator : public EntityCreator
 {
 public:
@@ -146,36 +167,6 @@
   }
   void connectEntities(const scene::Path& e1, const scene::Path& e2)
   {
-    class entity_maxtarget : public scene::Graph::Walker
-    {
-      unsigned int& m_maxtarget;
-    public:
-      entity_maxtarget(unsigned int& maxtarget)
-        : m_maxtarget(maxtarget)
-      {
-      }
-      bool pre(const scene::Path& path, scene::Instance& instance)
-      {
-        if(path.top()->m_entity)
-        {
-          const char* value = path.top()->m_entity->valueforkey("target");
-          if (value && value[0])
-          {
-            unsigned int targetnum = atoi(value+1);
-            if (targetnum > m_maxtarget)
-              m_maxtarget = targetnum;
-          }
-        }
-        return true;
-      }
-      void post(const scene::Path& path, scene::Instance& instance)
-      {
-      }
-    };
-
-    char newtarget[16];
-    unsigned int maxtarget = 0;  // highest t# value in the map
-
     if(e1.top()->m_entity == 0
       || e2.top()->m_entity == 0)
       return;
@@ -185,15 +176,30 @@
 		  return;
 	  }
 
+    if(g_gameType == eGameTypeDoom3)
     {
-      entity_maxtarget walker(maxtarget);
-      GlobalSceneGraph().traverse(walker);
+      StringOutputStream key(16);
+      for(unsigned int i = 0; ; ++i)
+      {
+        key << "target";
+        if(i != 0)
+        {
+           key << i;
+        }
+        const char* value = e1.top()->m_entity->valueforkey(key.c_str());
+        if(string_empty(value))
+        {
+          e1.top()->m_entity->setkeyvalue(key.c_str(), e2.top()->m_entity->valueforkey("name"));
+          break;
+        }
+        key.clear();
+      }
     }
-
-    sprintf (newtarget, "t%i", maxtarget+1);
-    
-	  e1.top()->m_entity->setkeyvalue("target", newtarget);
-	  e2.top()->m_entity->setkeyvalue("targetname", newtarget);
+    else
+    {
+      ConnectEntities connect(e1, e2);
+      GlobalNamespace().makeUnique("t1", ConnectEntities::ConnectCaller(connect));
+    }
   }
   void setLightRadii(bool lightRadii)
   {

Modified: GtkRadiant/trunk/plugins/entity/light.cpp
===================================================================
--- GtkRadiant/trunk/plugins/entity/light.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/light.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -542,14 +542,14 @@
 
   void lightRadiusChanged(const char* value)
   {
-    m_useRadiusKey = string_read_vector(value, m_radius);
+    m_useRadiusKey = string_parse_vector(value, m_radius);
     m_changed();
   }
   typedef MemberCaller1<Doom3LightRadius, const char*>::Caller<&Doom3LightRadius::lightRadiusChanged> LightRadiusChangedCaller;
 
   void lightCenterChanged(const char* value)
   {
-    m_useCenterKey = string_read_vector(value, m_center);
+    m_useCenterKey = string_parse_vector(value, m_center);
   }
   typedef MemberCaller1<Doom3LightRadius, const char*>::Caller<&Doom3LightRadius::lightCenterChanged> LightCenterChangedCaller;
 };

Modified: GtkRadiant/trunk/plugins/entity/namekeys.h
===================================================================
--- GtkRadiant/trunk/plugins/entity/namekeys.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/namekeys.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -3,6 +3,7 @@
 #define _INCLUDED_NAMEKEYS_H_
 
 #include <stdio.h>
+#include <map>
 #include "staticinstance.h"
 #include "entitylib.h"
 #include "namespace.h"
@@ -37,6 +38,40 @@
   KeyIsNameFunc m_keyIsName;
   NameKeys(const NameKeys& other);
   NameKeys& operator=(const NameKeys& other);
+
+  typedef std::map<string_t, EntityKeyValues::Value*> KeyValues;
+  KeyValues m_keyValues;
+
+  void insertName(const char* key, EntityKeyValues::Value& value)
+  {
+    if(m_namespace != 0 && m_keyIsName(key))
+    {
+      //globalOutputStream() << "insert " << key << "\n";
+      m_namespace->attach(KeyValueAssignCaller(value), KeyValueAttachCaller(value));
+    }
+  }
+  void eraseName(const char* key, EntityKeyValues::Value& value)
+  {
+    if(m_namespace != 0 && m_keyIsName(key))
+    {
+      //globalOutputStream() << "erase " << key << "\n";
+      m_namespace->detach(KeyValueAssignCaller(value), KeyValueDetachCaller(value));
+    }
+  }
+  void insertAll()
+  {
+    for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
+    {
+      insertName((*i).first.c_str(), *(*i).second);
+    }
+  }
+  void eraseAll()
+  {
+    for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
+    {
+      eraseName((*i).first.c_str(), *(*i).second);
+    }
+  }
 public:
   NameKeys(EntityKeyValues& entity) : m_namespace(0), m_entity(entity), m_keyIsName(Static<KeyIsName>::instance().m_keyIsName)
   {
@@ -48,31 +83,25 @@
   }
   void setNamespace(Namespace& space)
   {
-    m_entity.detach(*this);
+    eraseAll();
     m_namespace = &space;
-    m_entity.attach(*this);
+    insertAll();
   }
   void setKeyIsName(KeyIsNameFunc keyIsName)
   {
-    m_entity.detach(*this);
+    eraseAll();
     m_keyIsName = keyIsName;
-    m_entity.attach(*this);
+    insertAll();
   }
   void insert(const char* key, EntityKeyValues::Value& value)
   {
-    if(m_namespace != 0 && m_keyIsName(key))
-    {
-      //globalOutputStream() << "insert " << key << "\n";
-      m_namespace->attach(KeyValueAssignCaller(value), KeyValueAttachCaller(value));
-    }
+    m_keyValues.insert(KeyValues::value_type(key, &value));
+    insertName(key, value);
   }
   void erase(const char* key, EntityKeyValues::Value& value)
   {
-    if(m_namespace != 0 && m_keyIsName(key))
-    {
-      //globalOutputStream() << "erase " << key << "\n";
-      m_namespace->detach(KeyValueAssignCaller(value), KeyValueDetachCaller(value));
-    }
+    eraseName(key, value);
+    m_keyValues.erase(key);
   }
 };
 

Modified: GtkRadiant/trunk/plugins/entity/static.cpp
===================================================================
--- GtkRadiant/trunk/plugins/entity/static.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/static.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -60,11 +60,9 @@
 public:
   FuncStaticOrigin(TraversableSet& set, Origin& origin) : m_set(set), m_observer(0), m_origin(origin), m_enabled(false)
   {
-    m_set.attach(this);
   }
   ~FuncStaticOrigin()
   {
-    m_set.detach(this);
   }
 
   void enable()
@@ -121,57 +119,183 @@
   }
 };
 
-class FuncStaticModelNode : public scene::Traversable
+#include "math/curve.h"
+
+class RenderableCurve : public OpenGLRenderable
 {
-  scene::Traversable& m_set;
-  scene::Node* m_model;
-  bool m_enabled;
 public:
-  FuncStaticModelNode(scene::Traversable& set) : m_set(set), m_model(0), m_enabled(false)
+  std::vector<PointVertex> m_vertices;
+  void Render(ERenderState state) const
   {
+    pointvertex_gl_array(&m_vertices.front());
+    qglDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_vertices.size()));
   }
+};
 
-  void setEnabled(bool enabled)
+void plotBasisFunction(std::size_t numSegments, int point, int degree)
+{
+  Knots knots;
+  KnotVector_openUniform(knots, 4, degree);
+
+  globalOutputStream() << "plotBasisFunction point " << point << " of 4, knot vector:";
+  for(Knots::iterator i = knots.begin(); i != knots.end(); ++i)
   {
-    if(m_model != 0)
+    globalOutputStream() << " " << *i;
+  }
+  globalOutputStream() << "\n";
+  globalOutputStream() << "t=0 basis=" << BSpline_basis(knots, point, degree, 0.0) << "\n";
+  for(std::size_t i = 1; i < numSegments; ++i)
+  {
+    float t = (1.0 / double(numSegments)) * double(i);
+    globalOutputStream() << "t=" << t << " basis=" << BSpline_basis(knots, point, degree, t) << "\n";
+  }
+  globalOutputStream() << "t=1 basis=" << BSpline_basis(knots, point, degree, 1.0) << "\n";  
+}
+
+class NURBSCurve
+{
+public:
+  ControlPoints m_controlPoints;
+  NURBSWeights m_weights;
+  Knots m_knots;
+  RenderableCurve m_renderCurve;
+
+  bool parseCurve(const char* value)
+  {
+    StringTokeniser tokeniser(value, " ");
+
+    std::size_t size;
+    if(!string_parse_size(tokeniser.getToken(), size))
     {
-      if(m_enabled && !enabled)
+      return false;
+    }
+
+    if(size < 3)
+    {
+      return false;
+    }
+    const int BSpline_degree = 3;
+
+    m_weights.allocate(size);
+    for(NURBSWeights::iterator i = m_weights.begin(); i != m_weights.end(); ++i)
+    {
+      (*i) = 1;
+    }
+
+    m_controlPoints.allocate(size);
+
+    if(!string_equal(tokeniser.getToken(), "("))
+    {
+      return false;
+    }
+    for(ControlPoints::iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i)
+    {
+      if(!string_parse_float(tokeniser.getToken(), (*i).x())
+        || !string_parse_float(tokeniser.getToken(), (*i).y())
+        || !string_parse_float(tokeniser.getToken(), (*i).z()))
       {
-        m_set.erase(m_model);
+        return false;
       }
-      else if(!m_enabled && enabled)
-      {
-        m_set.insert(m_model);
-      }
     }
-    m_enabled = enabled;
+    if(!string_equal(tokeniser.getToken(), ")"))
+    {
+      return false;
+    }
+
+    KnotVector_openUniform(m_knots, m_controlPoints.size(), BSpline_degree);
+
+    const std::size_t numSegments = 64;
+    m_renderCurve.m_vertices.resize(numSegments + 1);
+    m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPoints[0]);
+    for(std::size_t i = 1; i < numSegments; ++i)
+    {
+      m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(NURBS_evaluate(m_controlPoints, m_weights, m_knots, BSpline_degree, (1.0 / double(numSegments)) * double(i)));
+    }
+    m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPoints[m_controlPoints.size() - 1]);
+
+    //plotBasisFunction(8, 0, BSpline_degree);
+
+    return true;
   }
 
-  void insert(scene::Node* node)
+  void curveChanged(const char* value)
   {
-    RADIANT_ASSERT(m_model == 0, "func_error");
-    m_model = node;
-    if(m_enabled)
+    if(string_empty(value) || !parseCurve(value))
     {
-      m_set.insert(node);
+      m_controlPoints.allocate(0);
+      m_knots.allocate(0);
+      m_weights.allocate(0);
+      m_renderCurve.m_vertices.clear();
     }
   }
-  void erase(scene::Node* node)
+  typedef MemberCaller1<NURBSCurve, const char*>::Caller<NURBSCurve::curveChanged> CurveChangedCaller;
+};
+
+class CatmullRomSpline
+{
+public:
+  ControlPoints m_controlPoints;
+  RenderableCurve m_renderCurve;
+
+  bool parseCurve(const char* value)
   {
-    RADIANT_ASSERT(m_model == node, "func_error");
-    m_model = 0;
-    if(m_enabled)
+    StringTokeniser tokeniser(value, " ");
+
+    std::size_t size;
+    if(!string_parse_size(tokeniser.getToken(), size))
     {
-      m_set.erase(node);
+      return false;
     }
+
+    if(size < 3)
+    {
+      return false;
+    }
+    const int spline_degree = 3;
+
+    m_controlPoints.allocate(size);
+
+    if(!string_equal(tokeniser.getToken(), "("))
+    {
+      return false;
+    }
+    for(ControlPoints::iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i)
+    {
+      if(!string_parse_float(tokeniser.getToken(), (*i).x())
+        || !string_parse_float(tokeniser.getToken(), (*i).y())
+        || !string_parse_float(tokeniser.getToken(), (*i).z()))
+      {
+        return false;
+      }
+    }
+    if(!string_equal(tokeniser.getToken(), ")"))
+    {
+      return false;
+    }
+
+    const std::size_t numSegments = 64;
+    m_renderCurve.m_vertices.resize(numSegments + 1);
+    m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPoints[0]);
+    for(std::size_t i = 1; i < numSegments; ++i)
+    {
+      m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(CatmullRom_evaluate(m_controlPoints, (1.0 / double(numSegments)) * double(i)));
+    }
+    m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPoints[m_controlPoints.size() - 1]);
+
+    return true;
   }
-  void traverse(Walker& walker)
+
+  void curveChanged(const char* value)
   {
-    m_set.traverse(walker);
+    if(string_empty(value) || !parseCurve(value))
+    {
+      m_controlPoints.allocate(0);
+      m_renderCurve.m_vertices.clear();
+    }
   }
+  typedef MemberCaller1<CatmullRomSpline, const char*>::Caller<CatmullRomSpline::curveChanged> CurveChangedCaller;
 };
 
-
 class FuncStatic : public Editable
 {
   EntityKeyValues m_entity;
@@ -179,9 +303,8 @@
   TraversableSet m_traverse;
   FuncStaticOrigin m_funcStaticOrigin;
   MatrixTransform m_transform;
-  FuncStaticModelNode m_modelNode;
 
-  Model m_model;
+  SingletonModel m_model;
   Origin m_origin;
   Angle m_angle;
   Rotation m_rotation;
@@ -190,14 +313,21 @@
   NamedEntity m_named;
   NameKeys m_nameKeys;
 
+  NURBSCurve m_curveNURBS;
+  CatmullRomSpline m_curveCatmullRom;
+
   Closure m_transformChanged;
 
   string_t m_name;
   string_t m_modelKey;
   bool m_isModel;
 
+  scene::Node* m_node;
+
   void construct(scene::Node* node)
   {
+    m_node = node;
+
     node->m_edit = this;
     node->m_transform = &m_transform;
     node->m_traverse = &m_traverse;
@@ -212,16 +342,50 @@
     m_keyObservers.insert("angle", Angle::AngleChangedCaller(m_angle));
     m_keyObservers.insert("rotation", Rotation::RotationChangedCaller(m_rotation));
     m_keyObservers.insert("name", NameChangedCaller(*this));
+    m_keyObservers.insert("curve_Nurbs", NURBSCurve::CurveChangedCaller(m_curveNURBS));
+    m_keyObservers.insert("curve_CatmullRomSpline", CatmullRomSpline::CurveChangedCaller(m_curveCatmullRom));
 
+    m_isModel = false;
+    m_nameKeys.setKeyIsName(keyIsNameDoom3FuncStatic);
+    attachTraverse();
+
     m_entity.attach(m_keyObservers);
-
-    m_isModel = false;
   }
   void destroy()
   {
     m_entity.detach(m_keyObservers);
+
+    if(isModel())
+    {
+      detachModel();
+    }
+    else
+    {
+      detachTraverse();
+    }
   }
 
+  void attachModel()
+  {
+    m_node->m_traverse = &m_model.getTraversable();
+    m_model.attach(&m_funcStaticOrigin);
+  }
+  void detachModel()
+  {
+    m_node->m_traverse = 0;
+    m_model.detach(&m_funcStaticOrigin);
+  }
+  void attachTraverse()
+  {
+    m_node->m_traverse = &m_traverse;
+    m_traverse.attach(&m_funcStaticOrigin);
+  }
+  void detachTraverse()
+  {
+    m_node->m_traverse = 0;
+    m_traverse.detach(&m_funcStaticOrigin);
+  }
+
   bool isModel()
   {
     return m_isModel;
@@ -229,14 +393,19 @@
  
   void setIsModel(bool newValue)
   {
-    m_modelNode.setEnabled(newValue);
     if(newValue && !m_isModel)
     {
+      detachTraverse();
+      attachModel();
+
       m_nameKeys.setKeyIsName(Static<KeyIsName>::instance().m_keyIsName);
       m_model.modelChanged(m_modelKey.c_str());
     }
     else if(!newValue && m_isModel)
     {
+      detachModel();
+      attachTraverse();
+
       m_nameKeys.setKeyIsName(keyIsNameDoom3FuncStatic);
     }
     m_isModel = newValue;
@@ -286,15 +455,16 @@
   void originChanged()
   {
     updateTransform();
-    m_funcStaticOrigin.originChanged();
+    if(!isModel())
+    {
+      m_funcStaticOrigin.originChanged();
+    }
   }
   typedef MemberCaller<FuncStatic>::Caller<&FuncStatic::originChanged> OriginChangedCaller;
 
 public:
   FuncStatic(EntityClass* eclass, scene::Node* node, const Closure& transformChanged) :
     m_entity(eclass),
-    m_modelNode(m_traverse),
-    m_model(m_modelNode, UpdateTransformCaller(*this)),
     m_origin(OriginChangedCaller(*this)),
     m_angle(UpdateTransformCaller(*this)),
     m_rotation(UpdateTransformCaller(*this)),
@@ -308,8 +478,6 @@
   }
   FuncStatic(const FuncStatic& other, scene::Node* node, const Closure& transformChanged) :
     m_entity(other.m_entity),
-    m_modelNode(m_traverse),
-    m_model(m_modelNode, UpdateTransformCaller(*this)),
     m_origin(OriginChangedCaller(*this)),
     m_angle(UpdateTransformCaller(*this)),
     m_rotation(UpdateTransformCaller(*this)),
@@ -371,6 +539,16 @@
   void render(IRenderer* renderer, const VolumeTest& volume, const Matrix4& local2world) const
   {
     renderer->SetState(m_entity.eclass().m_state_wire, IRenderer::eWireframeOnly);
+    renderer->SetState(m_entity.eclass().m_state_wire, IRenderer::eFullMaterials);
+
+    if(!m_curveNURBS.m_renderCurve.m_vertices.empty())
+    {
+      renderer->Add(&m_curveNURBS.m_renderCurve, local2world);
+    }
+    if(!m_curveCatmullRom.m_renderCurve.m_vertices.empty())
+    {
+      renderer->Add(&m_curveCatmullRom.m_renderCurve, local2world);
+    }
   }
 
   // edit

Modified: GtkRadiant/trunk/plugins/entity/targetable.cpp
===================================================================
--- GtkRadiant/trunk/plugins/entity/targetable.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/targetable.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -1,9 +1,6 @@
 
 #include "targetable.h"
 
-#include <map>
-
-
 typedef std::map<string_t, targetables_t> targetnames_t;
 
 const char* g_targetable_nameKey = "targetname";

Modified: GtkRadiant/trunk/plugins/entity/targetable.h
===================================================================
--- GtkRadiant/trunk/plugins/entity/targetable.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/entity/targetable.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -3,6 +3,7 @@
 #define _INCLUDE_TARGETABLE_H_
 
 #include <set>
+#include <map>
 
 #include "cullable.h"
 #include "renderable.h"
@@ -13,6 +14,7 @@
 #include "selectionlib.h"
 #include "entitylib.h"
 #include "eclasslib.h"
+#include "stringio.h"
 
 class Targetable
 {
@@ -105,7 +107,8 @@
 public:
   TargetingEntity()
     : m_targets(getTargetables(""))
-  {}
+  {
+  }
   void targetChanged(const char* target)
   {
     m_targets = getTargetables(target);
@@ -117,19 +120,25 @@
   iterator begin() const
   {
     if(m_targets == 0)
+    {
       return iterator();
+    }
     return m_targets->begin();
   }
   iterator end() const
   {
     if(m_targets == 0)
+    {
       return iterator();
+    }
     return m_targets->end();
   }
   size_t size() const
   {
     if(m_targets == 0)
+    {
       return 0;
+    }
     return m_targets->size();
   }
   bool empty() const
@@ -139,11 +148,60 @@
 };
 
 
+
+typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
+
+class TargetKeys : public EntityKeyValues::Observer
+{
+  TargetingEntities m_targetingEntities;
+
+  bool readTargetKey(const char* key, std::size_t& index)
+  {
+    if(string_equal_n(key, "target", 6))
+    {
+      index = 0;
+      if(string_empty(key + 6) || string_parse_size(key + 6, index))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+public:
+  void insert(const char* key, EntityKeyValues::Value& value)
+  {
+    std::size_t index;
+    if(readTargetKey(key, index))
+    {
+      TargetingEntities::iterator i = m_targetingEntities.insert(TargetingEntities::value_type(index, TargetingEntity())).first;
+      value.attach(TargetingEntity::TargetChangedCaller((*i).second));
+    }
+  }
+  void erase(const char* key, EntityKeyValues::Value& value)
+  {
+    std::size_t index;
+    if(readTargetKey(key, index))
+    {
+      TargetingEntities::iterator i = m_targetingEntities.find(index);
+      value.detach(TargetingEntity::TargetChangedCaller((*i).second));
+      m_targetingEntities.erase(i);
+    }
+  }
+  const TargetingEntities& get() const
+  {
+    return m_targetingEntities;
+  }
+};
+
+
+
 class RenderableTargetingEntity
 {
   TargetingEntity& m_targets;
   mutable RenderablePointVector m_target_lines;
 public:
+  static Shader* m_state;
+
   RenderableTargetingEntity(TargetingEntity& targets)
     : m_targets(targets), m_target_lines(GL_LINES)
   {
@@ -172,10 +230,50 @@
       renderer->Add(&m_target_lines, g_matrix4_identity);
     }
   }
+};
 
+class RenderableTargetingEntities
+{
+  const TargetingEntities& m_targets;
+  mutable RenderablePointVector m_target_lines;
+public:
   static Shader* m_state;
+
+  RenderableTargetingEntities(const TargetingEntities& targets)
+    : m_targets(targets), m_target_lines(GL_LINES)
+  {
+  }
+  void compile(const VolumeTest& volume, const Vector3& world_position) const
+  {
+    m_target_lines.clear();
+
+    for(TargetingEntities::const_iterator i = m_targets.begin(); i != m_targets.end(); ++i)
+    {
+      const TargetingEntity& targets = (*i).second;
+      for(TargetingEntity::iterator j = targets.begin(); j != targets.end(); ++j)
+      {
+        Vector3 start(world_position);
+        Vector3 end((*j)->world_position());
+        if(volume.TestLine(segment_for_startend(start, end)))
+        {
+          m_target_lines.push_back(PointVertex(reinterpret_cast<const vertex3f_t&>(start)));
+          m_target_lines.push_back(PointVertex(reinterpret_cast<const vertex3f_t&>(end)));
+        }
+      }
+    }
+  }
+  void Render(IRenderer* renderer, const VolumeTest& volume, const Vector3& world_position) const
+  {
+    if(!m_targets.empty())
+    {
+      compile(volume, world_position);
+
+      renderer->Add(&m_target_lines, g_matrix4_identity);
+    }
+  }
 };
 
+
 class TargetableInstance :
 public BaseSelectableInstance,
 public Targetable,
@@ -183,40 +281,34 @@
 {
   mutable vertex3f_t m_position;
   EntityKeyValues& m_entity;
-  TargetingEntity m_targeting;
+  TargetKeys m_targeting;
   TargetedEntity m_targeted;
-  RenderableTargetingEntity m_renderable;
+  RenderableTargetingEntities m_renderable;
 public:
 
   TargetableInstance(const scene::Path& path, scene::Instance* parent, EntityKeyValues& entity)
-    : BaseSelectableInstance(path, parent), m_entity(entity), m_renderable(m_targeting), m_targeted(*static_cast<Targetable*>(this))
+    : BaseSelectableInstance(path, parent), m_entity(entity), m_renderable(m_targeting.get()), m_targeted(*static_cast<Targetable*>(this))
   {
     m_entity.attach(*this);
+    m_entity.attach(m_targeting);
   }
   ~TargetableInstance()
   {
+    m_entity.detach(m_targeting);
     m_entity.detach(*this);
   }
 
   void insert(const char* key, EntityKeyValues::Value& value)
   {
-    if(string_equal(key, "target"))
+    if(string_equal(key, g_targetable_nameKey))
     {
-      value.attach(TargetingEntity::TargetChangedCaller(m_targeting));
-    }
-    else if(string_equal(key, g_targetable_nameKey))
-    {
       value.attach(TargetedEntity::TargetnameChangedCaller(m_targeted));
     }
   }
   void erase(const char* key, EntityKeyValues::Value& value)
   {
-    if(string_equal(key, "target"))
+    if(string_equal(key, g_targetable_nameKey))
     {
-      value.detach(TargetingEntity::TargetChangedCaller(m_targeting));
-    }
-    else if(string_equal(key, g_targetable_nameKey))
-    {
       value.detach(TargetedEntity::TargetnameChangedCaller(m_targeted));
     }
   }

Modified: GtkRadiant/trunk/plugins/md3model/md5.cpp
===================================================================
--- GtkRadiant/trunk/plugins/md3model/md5.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/plugins/md3model/md5.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -106,9 +106,19 @@
 
 typedef auto_array<MD5Weight> MD5Weights;
 
+typedef float MD5Component;
+typedef auto_array<MD5Component> MD5Components;
 
-bool MD5Model_parse(Model& model, Tokeniser* tokeniser)
+class MD5Frame
 {
+public:
+  MD5Components m_components;
+};
+
+typedef auto_array<MD5Weight> MD5Weights;
+
+bool MD5_parseVersion(Tokeniser* tokeniser)
+{
   {
     const char* versionKey = tokeniser->getToken();
     if(versionKey == 0 || !string_equal(versionKey, "MD5Version"))
@@ -125,6 +135,13 @@
       return false;
     }
   }
+
+  return true;
+}
+
+bool MD5Anim_parse(Tokeniser* tokeniser)
+{
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseVersion(tokeniser));
   tokeniser->nextLine();
 
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "commandline"));
@@ -132,11 +149,117 @@
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, commandline));
   tokeniser->nextLine();
 
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numFrames"));
+  std::size_t numFrames;
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numFrames));
+  tokeniser->nextLine();
+
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numJoints"));
   std::size_t numJoints;
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numJoints));
   tokeniser->nextLine();
 
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "frameRate"));
+  std::size_t frameRate;
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, frameRate));
+  tokeniser->nextLine();
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numAnimatedComponents"));
+  std::size_t numAnimatedComponents;
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numAnimatedComponents));
+  tokeniser->nextLine();
+
+  // parse heirarchy
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "hierarchy"));
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{"));
+  tokeniser->nextLine();
+
+  for(std::size_t i = 0; i < numJoints; ++i)
+  {
+    const char* name;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, name));
+    int parent;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseInteger(tokeniser, parent));
+    std::size_t flags;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, flags));
+    std::size_t index;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, index));
+    tokeniser->nextLine();
+  }
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}"));
+  tokeniser->nextLine();
+
+  // parse bounds
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "bounds"));
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{"));
+  tokeniser->nextLine();
+
+  for(std::size_t i = 0; i < numFrames; ++i)
+  {
+    Vector3 mins;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, mins));
+    Vector3 maxs;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, maxs));
+    tokeniser->nextLine();
+  }
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}"));
+  tokeniser->nextLine();
+
+  // parse baseframe
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "baseframe"));
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{"));
+  tokeniser->nextLine();
+
+  for(std::size_t i = 0; i < numJoints; ++i)
+  {
+    Vector3 position;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, position));
+    Vector3 rotation;
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, rotation));
+    tokeniser->nextLine();
+  }
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}"));
+  tokeniser->nextLine();
+
+  // parse frames
+  for(std::size_t i = 0; i < numFrames; ++i)
+  {
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "frame"));
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{"));
+    tokeniser->nextLine();
+
+    for(std::size_t i = 0; i < numAnimatedComponents; ++i)
+    {
+      float component;
+      MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, component));
+      tokeniser->nextLine();
+    }
+
+    MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}"));
+    tokeniser->nextLine();
+  }
+
+  return true;
+}
+
+bool MD5Model_parse(Model& model, Tokeniser* tokeniser)
+{
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseVersion(tokeniser));
+  tokeniser->nextLine();
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "commandline"));
+  const char* commandline;
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, commandline));
+  tokeniser->nextLine();
+
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numJoints"));
+  std::size_t numJoints;
+  MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numJoints));
+  tokeniser->nextLine();
+
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numMeshes"));
   std::size_t numMeshes;
   MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numMeshes));

Modified: GtkRadiant/trunk/radiant/brush.h
===================================================================
--- GtkRadiant/trunk/radiant/brush.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/brush.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -67,8 +67,8 @@
 
 void SceneChangeNotify();
 
-//#define BRUSH_CONNECTIVITY_DEBUG
-//#define BRUSH_DEGENERATE_DEBUG
+#define BRUSH_CONNECTIVITY_DEBUG 0
+#define BRUSH_DEGENERATE_DEBUG 0
 
 inline void print_vector3(const Vector3& v)
 {
@@ -1067,7 +1067,7 @@
     RADIANT_ASSERT(index < (unsigned int)numpoints, "update_move_planepts_vertex: invalid index");
 
     const int opposite = Winding_Opposite(winding(), index);
-    const int adjacent = (opposite+numpoints-1) % numpoints;
+    const int adjacent = Winding_wrap(winding(), opposite+numpoints-1);
     planePoints[0] = winding()[opposite].vertex;
     planePoints[1] = winding()[index].vertex;
     planePoints[2] = winding()[adjacent].vertex;
@@ -1077,19 +1077,26 @@
   void snapto(float snap)
   {
     undo_save();
+#if 0
     RADIANT_ASSERT(plane3_valid(m_plane.plane3()), "invalid plane before snap to grid");
-#if 0
     planepts_snap(m_plane.planePoints(), snap);
+    RADIANT_ASSERT(plane3_valid(m_plane.plane3()), "invalid plane after snap to grid");
 #else
-    PlanePoints planePoints;
-    update_move_planepts_vertex(0, planePoints);
-    vector3_snap(planePoints[0], snap);
-    vector3_snap(planePoints[1], snap);
-    vector3_snap(planePoints[2], snap);
-    assign_planepts(planePoints);
+    if(contributes())
+    {
+      PlanePoints planePoints;
+      update_move_planepts_vertex(0, planePoints);
+      vector3_snap(planePoints[0], snap);
+      vector3_snap(planePoints[1], snap);
+      vector3_snap(planePoints[2], snap);
+      assign_planepts(planePoints);
+    }
 #endif
     m_plane.MakePlane();
-    RADIANT_ASSERT(plane3_valid(m_plane.plane3()), "invalid plane after snap to grid");
+    if(!plane3_valid(m_plane.plane3()))
+    {
+      globalErrorStream() << "WARNING: invalid plane after snap to grid";
+    }
     m_observer->planeChanged();
   }
 
@@ -2919,12 +2926,25 @@
       (*i)->vertex_clear();
   }
 
+  inline bool plane3_inside(const Plane3& self, const Plane3& other) const
+  {
+    if(vector3_equal_epsilon(self.normal(), other.normal(), 0.001))
+    {
+      return self.dist() < other.dist();
+    }
+    return true;
+  }
+
   bool plane_unique(unsigned int index) const
   {
     // duplicate plane
-    for(unsigned int i = 0; i < index; ++i)
-      if(plane3_equal(m_faces[index]->plane3(), m_faces[i]->plane3()))
+    for(unsigned int i = 0; i < m_faces.size(); ++i)
+    {
+      if(index != i && !plane3_inside(m_faces[index]->plane3(), m_faces[i]->plane3()))
+      {
         return false;
+      }
+    }
     return true;
   }
 
@@ -2944,7 +2964,7 @@
         std::size_t next = Winding_next(w, static_cast<unsigned int>(index));
         if(Edge_isDegenerate(w[index].vertex, w[next].vertex))
         {
-#if defined(BRUSH_DEGENERATE_DEBUG)
+#if BRUSH_DEGENERATE_DEBUG
           globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate edge adjacent to " << w[index].adjacent << "\n";
 #endif
           winding_t& other = m_faces[w[index].adjacent]->winding();
@@ -2972,7 +2992,7 @@
       
       if(degen.numpoints == 2)
       {
-#if defined(BRUSH_DEGENERATE_DEBUG)
+#if BRUSH_DEGENERATE_DEBUG
         globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate winding adjacent to " << degen[0].adjacent << ", " << degen[1].adjacent << "\n";
 #endif
         // this is an "edge" face, where the plane touches the edge of the brush
@@ -2981,7 +3001,7 @@
           unsigned int index = Winding_FindAdjacent(w, i);
           if(index != MAX_POINTS_ON_WINDING)
           {
-#if defined(BRUSH_DEGENERATE_DEBUG)
+#if BRUSH_DEGENERATE_DEBUG
             globalOutputStream() << "Brush::buildWindings: face " << degen[0].adjacent << ": remapping adjacent " << w[index].adjacent << " to " << degen[1].adjacent << "\n";
 #endif
             w[index].adjacent = degen[1].adjacent;
@@ -2993,7 +3013,7 @@
           unsigned int index = Winding_FindAdjacent(w, i);
           if(index != MAX_POINTS_ON_WINDING)
           {
-#if defined(BRUSH_DEGENERATE_DEBUG)
+#if BRUSH_DEGENERATE_DEBUG
             globalOutputStream() << "Brush::buildWindings: face " << degen[1].adjacent << ": remapping adjacent " << w[index].adjacent << " to " << degen[0].adjacent << "\n";
 #endif
             w[index].adjacent = degen[0].adjacent;
@@ -3018,7 +3038,7 @@
           std::size_t next = Winding_next(w, j);
           if(w[j].adjacent == w[next].adjacent)
           {
-#if defined(BRUSH_DEGENERATE_DEBUG)
+#if BRUSH_DEGENERATE_DEBUG
             globalOutputStream() << "Brush::buildWindings: face " << i << ": removed duplicate edge adjacent to face " << w[j].adjacent << "\n";
 #endif
             w.erase(w.begin() + next);
@@ -3042,14 +3062,14 @@
         winding_t& w = m_faces[i]->winding();
         for(winding_t::iterator j = w.begin(); j != w.end();)
         {
-#if defined(BRUSH_CONNECTIVITY_DEBUG)
+#if BRUSH_CONNECTIVITY_DEBUG
           globalOutputStream() << "Brush::buildWindings: face " << i << ": adjacent to face " << (*j).adjacent << "\n";
 #endif
           // remove unidirectional graph edges
           if((*j).adjacent == c_brush_maxFaces
             || Winding_FindAdjacent(m_faces[(*j).adjacent]->winding(), i) == MAX_POINTS_ON_WINDING)
           {
-#if defined(BRUSH_CONNECTIVITY_DEBUG)
+#if BRUSH_CONNECTIVITY_DEBUG
             globalOutputStream() << "Brush::buildWindings: face " << i << ": removing unidirectional connectivity graph edge adjacent to face " << (*j).adjacent << "\n";
 #endif
             w.erase(j);

Modified: GtkRadiant/trunk/radiant/eclass_doom3.cpp
===================================================================
--- GtkRadiant/trunk/radiant/eclass_doom3.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/eclass_doom3.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -91,6 +91,8 @@
   bool m_resolved;
   string_t m_mesh;
   string_t m_parent;
+  typedef std::map<string_t, string_t> Anims;
+  Anims m_anims;
   Model() : m_resolved(false)
   {
   }
@@ -181,8 +183,10 @@
     }
     else if(string_equal(parameter, "anim"))
     {
-      const char* animName = tokeniser->getToken();
+      string_t animName(tokeniser->getToken());
       const char* animFile = tokeniser->getToken();
+      model.m_anims.insert(Model::Anims::value_type(animName, animFile));
+
       const char* token = tokeniser->getToken();
 
       while(string_equal(token, ","))
@@ -228,6 +232,8 @@
   RADIANT_ASSERT(string_equal(token, "{"), "error parsing entity definition");
   tokeniser->nextLine();
 
+  StringOutputStream usage(256);
+
   for(;;)
   {
     const char* key = tokeniser->getToken();
@@ -244,7 +250,7 @@
     else if(string_equal(key, "editor_color"))
     {
       entityClass->colorSpecified = true;
-      bool success = string_read_vector(tokeniser->getToken(), entityClass->color);
+      bool success = string_parse_vector(tokeniser->getToken(), entityClass->color);
       RADIANT_ASSERT(success, "editor_color: parse error");
     }
     else if(string_equal(key, "editor_ragdoll"))
@@ -258,7 +264,7 @@
       if(!string_equal(value, "?"))
       {
         entityClass->fixedsize = true;
-        bool success = string_read_vector(value, entityClass->mins);
+        bool success = string_parse_vector(value, entityClass->mins);
         RADIANT_ASSERT(success, "editor_mins: parse error");
       }
     }
@@ -269,44 +275,49 @@
       if(!string_equal(value, "?"))
       {
         entityClass->fixedsize = true;
-        bool success = string_read_vector(value, entityClass->maxs);
+        bool success = string_parse_vector(value, entityClass->maxs);
         RADIANT_ASSERT(success, "editor_maxs: parse error");
       }
     }
     else if(string_equal(key, "editor_usage"))
     {
       const char* value = tokeniser->getToken();
-      entityClass->m_comments = value;
+      usage << value;
     }
+    else if(string_equal_n(key, "editor_usage", 12))
+    {
+      const char* value = tokeniser->getToken();
+      usage << "\n" << value;
+    }
     else if(string_equal_n(key, "editor_var ", 11))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("string", key + 11, key + 11));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal_n(key, "editor_snd ", 11))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("sound", key + 11, key + 11));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal_n(key, "editor_bool ", 12))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("boolean", key + 12, key + 12));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal_n(key, "editor_model ", 13))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("model", key + 13, key + 13));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal_n(key, "editor_color ", 13))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("color", key + 13, key + 13));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal_n(key, "editor_material ", 16))
     {
       entityClass->m_attributes.push_back(EntityClassAttribute("shader", key + 16, key + 16));
-      const char* value = tokeniser->getToken();
+      entityClass->m_attributes.back().m_description = tokeniser->getToken();
     }
     else if(string_equal(key, "inherit"))
     {
@@ -321,6 +332,15 @@
     tokeniser->nextLine();
   }
 
+  usage << "\n";
+
+  for(EntityClassAttributes::iterator i = entityClass->m_attributes.begin(); i != entityClass->m_attributes.end(); ++i)
+  {
+    usage << "\n" << (*i).m_name.c_str() << ": " << (*i).m_description.c_str();
+  }
+
+  entityClass->m_comments = usage.c_str();
+
   EntityClassDoom3_insertUnique(entityClass);
 }
 

Modified: GtkRadiant/trunk/radiant/entityinspector.cpp
===================================================================
--- GtkRadiant/trunk/radiant/entityinspector.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/entityinspector.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -111,6 +111,60 @@
   virtual void release() = 0;
 };
 
+class BooleanAttribute : public EntityAttribute
+{
+public:
+  string_t m_key;
+  GtkWidget* m_check;
+
+  static gboolean toggled(GtkWidget *widget, BooleanAttribute* self)
+  {
+    self->apply();
+    return FALSE;
+  }
+public:
+  BooleanAttribute(const char* key, const char* name) :
+    m_key(key),
+    m_check(0)
+  {
+    GtkWidget* check = gtk_check_button_new();
+    gtk_widget_show(check);
+
+    m_check = check;
+
+    guint handler = g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(toggled), this);
+    g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler));
+
+    update();
+
+    EntityInspector_appendAttribute(name, check);
+  }
+  void release()
+  {
+    delete this;
+  }
+  void apply()
+  {
+    Scene_EntitySetKeyValue_Selected_Undoable(m_key.c_str(), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_check)) ? "1" : "0");
+  }
+  typedef MemberCaller<BooleanAttribute>::Caller<&BooleanAttribute::apply> ApplyCaller;
+
+  void update()
+  {
+    KeyValues::const_iterator i = display_entity.find(m_key.c_str());
+    if(i != display_entity.end())
+    {
+      toggle_button_set_active_no_signal(m_check, atoi((*i).second.c_str()) != 0);
+    }
+    else
+    {
+      toggle_button_set_active_no_signal(m_check, false);
+    }
+  }
+  typedef MemberCaller<BooleanAttribute>::Caller<&BooleanAttribute::update> UpdateCaller;
+};
+
+
 class StringAttribute : public EntityAttribute
 {
 public:
@@ -618,11 +672,14 @@
   {
     if(string_equal((*i).m_type.c_str(), "string")
       || string_equal((*i).m_type.c_str(), "shader")
-      || string_equal((*i).m_type.c_str(), "boolean")
       || string_equal((*i).m_type.c_str(), "color"))
     {
       g_entityAttributes.push_back(new StringAttribute((*i).m_key.c_str(), (*i).m_name.c_str()));
     }
+    else if(string_equal((*i).m_type.c_str(), "boolean"))
+    {
+      g_entityAttributes.push_back(new BooleanAttribute((*i).m_key.c_str(), (*i).m_name.c_str()));
+    }
     else if(string_equal((*i).m_type.c_str(), "angle"))
     {
       g_entityAttributes.push_back(new AngleAttribute((*i).m_key.c_str(), (*i).m_name.c_str()));

Modified: GtkRadiant/trunk/radiant/mainframe.cpp
===================================================================
--- GtkRadiant/trunk/radiant/mainframe.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/mainframe.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -711,14 +711,34 @@
 }
 typedef FreeCaller1<const BoolImportClosure&, ShowWorkzoneExport> ShowWorkzoneExportCaller;
 
-ToggleItem g_show_names = ShowNamesExportCaller();
-ToggleItem g_show_angles = ShowAnglesExportCaller();
-ToggleItem g_show_blocks = ShowBlocksExportCaller();
-ToggleItem g_show_coordinates = ShowCoordinatesExportCaller();
-ToggleItem g_show_outline = ShowOutlineExportCaller();
-ToggleItem g_show_axes = ShowAxesExportCaller();
-ToggleItem g_show_workzone = ShowWorkzoneExportCaller();
+ShowNamesExportCaller g_show_names_caller;
+BoolExportClosure g_show_names_closure(g_show_names_caller);
+ToggleItem g_show_names(g_show_names_closure);
 
+ShowAnglesExportCaller g_show_angles_caller;
+BoolExportClosure g_show_angles_closure(g_show_angles_caller);
+ToggleItem g_show_angles(g_show_angles_closure);
+
+ShowBlocksExportCaller g_show_blocks_caller;
+BoolExportClosure g_show_blocks_closure(g_show_blocks_caller);
+ToggleItem g_show_blocks(g_show_blocks_closure);
+
+ShowCoordinatesExportCaller g_show_coordinates_caller;
+BoolExportClosure g_show_coordinates_closure(g_show_coordinates_caller);
+ToggleItem g_show_coordinates(g_show_coordinates_closure);
+
+ShowOutlineExportCaller g_show_outline_caller;
+BoolExportClosure g_show_outline_closure(g_show_outline_caller);
+ToggleItem g_show_outline(g_show_outline_closure);
+
+ShowAxesExportCaller g_show_axes_caller;
+BoolExportClosure g_show_axes_closure(g_show_axes_caller);
+ToggleItem g_show_axes(g_show_axes_closure);
+
+ShowWorkzoneExportCaller g_show_workzone_caller;
+BoolExportClosure g_show_workzone_closure(g_show_workzone_caller);
+ToggleItem g_show_workzone(g_show_workzone_closure);
+
 void XYShow_registerCommands()
 {
   global_toggles_insert("ShowAngles", ShowAnglesToggleCaller(), ToggleItem::AddClosureCaller(g_show_angles));
@@ -1175,10 +1195,18 @@
   }
 };
 
-ToggleItem g_edgeMode_button = FreeCaller1<const BoolImportClosure&, BoolFunctionExport<EdgeMode>::apply>();
-ToggleItem g_vertexMode_button = FreeCaller1<const BoolImportClosure&, BoolFunctionExport<VertexMode>::apply>();
-ToggleItem g_faceMode_button = FreeCaller1<const BoolImportClosure&, BoolFunctionExport<FaceMode>::apply>();
+FreeCaller1<const BoolImportClosure&, BoolFunctionExport<EdgeMode>::apply> g_edgeMode_button_caller;
+BoolExportClosure g_edgeMode_button_closure(g_edgeMode_button_caller);
+ToggleItem g_edgeMode_button(g_edgeMode_button_closure);
 
+FreeCaller1<const BoolImportClosure&, BoolFunctionExport<VertexMode>::apply> g_vertexMode_button_caller;
+BoolExportClosure g_vertexMode_button_closure(g_vertexMode_button_caller);
+ToggleItem g_vertexMode_button(g_vertexMode_button_closure);
+
+FreeCaller1<const BoolImportClosure&, BoolFunctionExport<FaceMode>::apply> g_faceMode_button_caller;
+BoolExportClosure g_faceMode_button_closure(g_faceMode_button_caller);
+ToggleItem g_faceMode_button(g_faceMode_button_closure);
+
 void ComponentModeChanged()
 {
   g_edgeMode_button.update();
@@ -1458,12 +1486,26 @@
   importClosure(GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip);
 }
 
-ToggleItem g_translatemode_button = FreeCaller1<const BoolImportClosure&, TranslateToolExport>();
-ToggleItem g_rotatemode_button = FreeCaller1<const BoolImportClosure&, RotateToolExport>();
-ToggleItem g_scalemode_button = FreeCaller1<const BoolImportClosure&, ScaleToolExport>();
-ToggleItem g_dragmode_button = FreeCaller1<const BoolImportClosure&, DragToolExport>();
-ToggleItem g_clipper_button = FreeCaller1<const BoolImportClosure&, ClipperToolExport>();
+FreeCaller1<const BoolImportClosure&, TranslateToolExport> g_translatemode_button_caller;
+BoolExportClosure g_translatemode_button_closure(g_translatemode_button_caller);
+ToggleItem g_translatemode_button(g_translatemode_button_closure);
 
+FreeCaller1<const BoolImportClosure&, RotateToolExport> g_rotatemode_button_caller;
+BoolExportClosure g_rotatemode_button_closure(g_rotatemode_button_caller);
+ToggleItem g_rotatemode_button(g_rotatemode_button_closure);
+
+FreeCaller1<const BoolImportClosure&, ScaleToolExport> g_scalemode_button_caller;
+BoolExportClosure g_scalemode_button_closure(g_scalemode_button_caller);
+ToggleItem g_scalemode_button(g_scalemode_button_closure);
+
+FreeCaller1<const BoolImportClosure&, DragToolExport> g_dragmode_button_caller;
+BoolExportClosure g_dragmode_button_closure(g_dragmode_button_caller);
+ToggleItem g_dragmode_button(g_dragmode_button_closure);
+
+FreeCaller1<const BoolImportClosure&, ClipperToolExport> g_clipper_button_caller;
+BoolExportClosure g_clipper_button_closure(g_clipper_button_caller);
+ToggleItem g_clipper_button(g_clipper_button_closure);
+
 void ToolChanged()
 {
   g_translatemode_button.update();
@@ -3450,8 +3492,8 @@
   global_commands_insert("ShowHidden", FreeCaller<Select_ShowAllHidden>(), Accelerator('H', (GdkModifierType)GDK_SHIFT_MASK));
   global_commands_insert("HideSelected", FreeCaller<HideSelected>(), Accelerator('H'));
 
-  global_toggles_insert("DragVertices", FreeCaller<SelectVertexMode>(), ToggleItem::AddClosureCaller(g_vertexMode_button), Accelerator('E'));
-  global_toggles_insert("DragEdges", FreeCaller<SelectEdgeMode>(), ToggleItem::AddClosureCaller(g_edgeMode_button), Accelerator('V'));
+  global_toggles_insert("DragVertices", FreeCaller<SelectVertexMode>(), ToggleItem::AddClosureCaller(g_vertexMode_button), Accelerator('V'));
+  global_toggles_insert("DragEdges", FreeCaller<SelectEdgeMode>(), ToggleItem::AddClosureCaller(g_edgeMode_button), Accelerator('E'));
   global_toggles_insert("DragFaces", FreeCaller<SelectFaceMode>(), ToggleItem::AddClosureCaller(g_faceMode_button), Accelerator('F'));
 
   global_commands_insert("MirrorSelectionX", FreeCaller<Selection_Flipx>());
@@ -3469,8 +3511,8 @@
   global_commands_insert("FindBrush", FreeCaller<DoFind>());
 
   global_commands_insert("MapInfo", FreeCaller<DoMapInfo>(), Accelerator('M'));
-  global_commands_insert("EntityColor", FreeCaller<Entity_setColour>(), Accelerator('K', (GdkModifierType)GDK_CONTROL_MASK));
-  global_commands_insert("ConnectSelection", FreeCaller<ConnectEntities>(), Accelerator('K'));
+  global_commands_insert("EntityColor", FreeCaller<Entity_setColour>(), Accelerator('K'));
+  global_commands_insert("ConnectSelection", FreeCaller<ConnectEntities>(), Accelerator('K', (GdkModifierType)GDK_CONTROL_MASK));
 
 
   global_toggles_insert("ToggleClipper", FreeCaller<ClipperMode>(), ToggleItem::AddClosureCaller(g_clipper_button), Accelerator('X'));

Modified: GtkRadiant/trunk/radiant/map.cpp
===================================================================
--- GtkRadiant/trunk/radiant/map.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/map.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -152,17 +152,24 @@
     std::pair<Names::iterator, bool> result = m_names.insert(Names::value_type(setName, m_uniqueNames));
     RADIANT_ASSERT(result.second, "cannot attach name");
     attachObserver(NameObserver::NameChangedCaller((*result.first).second));
-    globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
+    //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
   }
   void detach(const NameClosure& setName, const NameClosureClosure& detachObserver)
   {
     Names::iterator i = m_names.find(setName);
     RADIANT_ASSERT(i != m_names.end(), "cannot detach name");
-    globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
+    //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
     detachObserver(NameObserver::NameChangedCaller((*i).second));
     m_names.erase(i);
   }
 
+  void makeUnique(const char* name, const NameClosure& setName) const
+  {
+    char buffer[1024];
+    name_write(buffer, m_uniqueNames.make_unique(name_read(name)));
+    setName(buffer);
+  }
+
   void mergeNames(const BasicNamespace& other) const
   {
     typedef std::list<NameClosure> SetNameClosures;
@@ -502,7 +509,7 @@
   if (entity)
   {
     Vector3 origin;
-    string_read_vector(entity->valueforkey("origin"), origin);
+    string_parse_vector(entity->valueforkey("origin"), origin);
     FocusViews(origin, string_read_float(entity->valueforkey("angle")));
   }
   else
@@ -2082,7 +2089,7 @@
     entity_find walker(entity, path);
     path.top()->m_traverse->traverse(walker);
   }
-  if(path.size())
+  if(path.size() && path.top()->m_traverse != 0)
   {
     brush_find walker(brush, path);
     path.top()->m_traverse->traverse(walker);
@@ -2091,11 +2098,12 @@
 
 void SelectBrush (int entitynum, int brushnum)
 {
-  scene::Path brushpath;
-  Scene_FindEntityBrush(entitynum, brushnum, brushpath);
-  if(brushpath.size())
+  scene::Path path;
+  Scene_FindEntityBrush(entitynum, brushnum, path);
+  if(path.size())
   {
-    scene::Instance& instance = *GlobalSceneGraph().find(brushpath);
+    scene::Instance& instance = *GlobalSceneGraph().find(path);
+    RADIANT_ASSERT(instance.selectable() != 0, "SelectBrush: object not selectable");
     instance.selectable()->select(true);
     g_pParentWnd->GetXYWnd()->PositionView(instance.aabb_world().origin);
   }

Modified: GtkRadiant/trunk/radiant/referencecache.cpp
===================================================================
--- GtkRadiant/trunk/radiant/referencecache.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/referencecache.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -331,7 +331,7 @@
         }
         else
         {
-          if(string_not_empty(m_name.c_str()))
+          if(string_not_empty(m_type.c_str()))
           {
             globalErrorStream() << "Model type not supported: \"" << m_name.c_str() << "\"\n";
           }

Modified: GtkRadiant/trunk/radiant/texwindow.cpp
===================================================================
--- GtkRadiant/trunk/radiant/texwindow.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/texwindow.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -870,10 +870,18 @@
 }
 typedef FreeCaller1<const BoolImportClosure&, TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
 
-ToggleItem g_textureswindow_hideunused_item = TextureBrowserHideUnusedExport();
-ToggleItem g_textureswindow_showshaders_item = TextureBrowserShowShadersExport();
-ToggleItem g_textureswindow_showshaderlistonly_item = TextureBrowserShowShaderlistOnlyExport();
+TextureBrowserHideUnusedExport g_textureswindow_hideunused_item_caller;
+BoolExportClosure g_textureswindow_hideunused_item_closure(g_textureswindow_hideunused_item_caller);
+ToggleItem g_textureswindow_hideunused_item(g_textureswindow_hideunused_item_closure);
 
+TextureBrowserShowShadersExport g_textureswindow_showshaders_item_caller;
+BoolExportClosure g_textureswindow_showshaders_item_closure(g_textureswindow_showshaders_item_caller);
+ToggleItem g_textureswindow_showshaders_item(g_textureswindow_showshaders_item_closure);
+
+TextureBrowserShowShaderlistOnlyExport g_textureswindow_showshaderlistonly_item_caller;
+BoolExportClosure g_textureswindow_showshaderlistonly_item_closure(g_textureswindow_showshaderlistonly_item_caller);
+ToggleItem g_textureswindow_showshaderlistonly_item(g_textureswindow_showshaderlistonly_item_closure);
+
 void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnused)
 {
   if(hideUnused)

Modified: GtkRadiant/trunk/radiant/winding.cpp
===================================================================
--- GtkRadiant/trunk/radiant/winding.cpp	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/winding.cpp	2004-09-21 18:46:46 UTC (rev 4792)
@@ -278,7 +278,7 @@
 
 unsigned int Winding_Opposite(const winding_t& w, const unsigned int index)
 {
-  return Winding_Opposite(w, index, (index+1) % w.numpoints);
+  return Winding_Opposite(w, index, Winding_next(w, index));
 }
 
 void Winding_Centroid(const winding_t& w, const Plane3& plane, Vector3& centroid)

Modified: GtkRadiant/trunk/radiant/winding.h
===================================================================
--- GtkRadiant/trunk/radiant/winding.h	2004-09-16 18:14:45 UTC (rev 4791)
+++ GtkRadiant/trunk/radiant/winding.h	2004-09-21 18:46:46 UTC (rev 4792)
@@ -221,8 +221,15 @@
 
 };
 
+inline unsigned int Winding_wrap(const winding_t& winding, unsigned int i)
+{
+  RADIANT_ASSERT(winding.numpoints != 0, "Winding_wrap: empty winding");
+  return i % winding.numpoints;
+}
+
 inline unsigned int Winding_next(const winding_t& winding, unsigned int i)
 {
+  RADIANT_ASSERT(winding.numpoints != 0, "Winding_next: empty winding");
   return ++i % winding.numpoints;
 }
 




More information about the Gtkradiant mailing list