External properties (or how to avoid ugly class with 200 properties related to higher level subsystems)

I have came across an interesting issue latest days, which I will first describe the naive way !

Let's say I have a Geometry class, which holds GPU hardware buffers and some render variables.

  • The next step is realizing that collision data is needed. So let's add a pointer to collision data.
  • Then come the time to realize that in editor mode, access to source mesh would be needed. OK. Well, let's add a reference to a Mesh class holding triangle data in system memory.
  • Well, now it's time to implement culling. Let's add AABB to the Geometry class.
  • Damn, sphere tree would be nice too, since my engine could also support that.
  • And again...
  • And again...
  • Now there is an horrible Geometry with hundreds of definitions to variables that Geometry shouldn't even know about (even though they are for sure logically bound to this geometry, but they break lot of OO principle and add a lot of coupling). In fact, collision and culling system are way higher level than the system in which Geometry is defined. How to escape that hell ?

Well, I realized that a system similiar to WPF with its DependencyProperty and DependencyObject would fit perfectly ! Objects that have such external variables should either inherits from a specific class or have a member of that class, which will act as an external property container. Performance shouldn't be a concern, as it could be very fast (pre-sorted) and it's very high level : bottlenecks should never happens there.

The end result looks like :

 geometry.SetValue(SourceMeshProperty, sourceMesh); ICollisionShape collisionShape = geometry.GetValue(Collision.CollisionShapeProperty); 

A very nice feature that come from it is the ability to setup delegates to build default value of such properties (WPF could only return a static value independant of the object). As an example, I don't even need to build my collision shapes in advance : a thread could get them updated in background, and if it's not done when required the appropriate construction delegate would be fired.