Ok, with your help - snk_kid - I have decided to go for this design:
//Instead of adding functions to the Node base class//The Node base class will execute NodeOperations//Kind of like the Visitor Pattern, except I will //use RTTI to decide the type of the node - new//Node types will be allowed...class NodeOperation{ public virtual void Execute(Node node) { }}/*For eg. this RenderOperation takes the place ofNode::Render(RenderList renderer);*/class RenderOperation : NodeOperation{ private RenderList m_renderer; public RenderOperation(RenderList renderer) { m_renderer = renderer; } public override void Execute(Node node) { GeometryNode gNode = (node as GeometryNode); if (gNode != null) { if(m_list.cull(gNode.GetBoundingVolume())) { m_renderer.Draw(gNode.GetGeometry()); } //count++; //System.Console.WriteLine(gNode.m_name); } } private uint count = 0;}//The Node base class, Instead of adding functions Like://virtual Render(RenderList renderer);//virtual GetBoundingVolume();//... and other similar functions//I will have the ExecuteOperation(NodeOperation op) function//That dispatches the operation.abstract class Node{ public virtual void ExecuteOperation(NodeOperation op) { op.Execute(this); }}//The Group node, according to the Composite pattern //just a basic version :)class Group : Node{ private LinkedList<Node> m_children = new LinkedList<Node>(); public override void ExecuteOperation(NodeOperation op) { LinkedListNode<Node> currentChild = m_children.Head; for (int i = 0; i < m_children.Count; ++i) { currentChild.Value.ExecuteOperation(op); currentChild = currentChild.Next; } } public void AddChild(Node child) { m_children.AddTail(new LinkedListNode<Node>(child)); }}//Transformation group, holds transformation//infoclass Trasformation : Group{ private Matrix m_transformation; public Trasformation(Matrix someTransform) { m_transformation = someTransform; }}//The GeometryNode, leaf node according to the Composite pattern//Will hold some geometry info...class GeometryNode : Node{ private Geometry m_geometry; public GeometryNode(Geometry geo) { m_geometry = geo; } public Geometry GetGeometry() { return m_geometry; }}
Unfortunately, I can't think of any way of eliminating the need for RTTI :(
Which is used in the RenderOperation::Execute(...) to check if the node
is a Geometry.
I have actually benchmarked the use of the ExecuteOperation(NodeOperation op)
vs having multiple functions Render(...) GetBoundingVolume() etc...
And in c# it made no difference in performance, even though the use of "as operator".
Of course I'm not saying that my benchmark was precice... I just executed
the RenderOperation 10000000 times...
This will give me the flexibility of adding new Operations but also new Node types, without the need of recompiling all old modules that use the Node class.
So the RenderOperation would be executed like this:
Trasformation root = new Trasformation(new Matrix(...));root.AddChild(new GeometryNode(pSomeGeometry)); Trasformation transform = new Trasformation(new Matrix(...));transform.AddChild(new GeometryNode(pSomeGeometry2)); root.AddChild(transform);/* now I want to render the nodes starting from root */RenderList list = new RenderList();RenderOperation render = new RenderOperation(list); root.ExecuteOperation(render);
How does that seem?
Thank you for your help snk_kid, rated you as extremely helpful :) heh