From 72d87b7cc5426d91553809a46b6b379a5a976e1f Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Tue, 6 Jun 2006 01:31:36 +0000
Subject: [PATCH] More functions implemented.  NiNode and NiObjectNET are now
 good examples of how to deal with pointers. With implementation of
 NiNode::GetChildren function, I was able to re-enable the building up of the
 bind position.

---
 gen/obj_defines.h        |  4 ++--
 niflib.cpp               | 30 +++++++++++++++---------------
 obj/NiAVObject.cpp       |  7 +++++--
 obj/NiAVObject.h         |  3 +--
 obj/NiNode.cpp           | 35 ++++++++++++++++++++++++++++++++++-
 obj/NiNode.h             |  6 ++++++
 obj/NiObjectNET.cpp      | 12 +++++++++++-
 obj/NiTimeController.cpp | 10 +++++++++-
 obj/NiTimeController.h   | 12 ++++++++++++
 9 files changed, 95 insertions(+), 24 deletions(-)

diff --git a/gen/obj_defines.h b/gen/obj_defines.h
index b42cf597..e615342a 100644
--- a/gen/obj_defines.h
+++ b/gen/obj_defines.h
@@ -1399,7 +1399,7 @@ float frequency; \
 float phase; \
 float startTime; \
 float stopTime; \
-NiObject * target; \
+NiObjectNET * target; \
 
 #define NI_TIME_CONTROLLER_INCLUDE "NiObject.h" \
 
@@ -1463,7 +1463,7 @@ link_stack.pop_front(); \
 if (link_stack.empty()) \
 	throw runtime_error("Trying to pop a link from empty stack. This is probably a bug."); \
 if (link_stack.front() != 0xffffffff) { \
-	target = DynamicCast<NiObject>(objects[link_stack.front()]); \
+	target = DynamicCast<NiObjectNET>(objects[link_stack.front()]); \
 	if ( target == NULL ) \
 		throw runtime_error("Link could not be cast to required type during file read. This NIF file may be invalid or improperly understood."); \
 } else \
diff --git a/niflib.cpp b/niflib.cpp
index f3508d44..4a81f07a 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -17,7 +17,7 @@ map<string, blk_factory_func> global_block_map;
 
 //Utility Functions
 void EnumerateObjects( NiObjectRef const & root, map<Type*,uint> & type_map, map<NiObjectRef, uint> & link_map );
-void BuildUpBindPositions( const NiAVObjectRef & av );
+void BuildUpBindPositions( const NiAVObjectRef & root );
 NiObjectRef FindRoot( vector<NiObjectRef> const & blocks );
 void RegisterBlockFactories ();
 NiObjectRef GetObjectByType( const NiObjectRef & root, const Type & block_type );
@@ -509,30 +509,30 @@ void EnumerateObjects( NiObjectRef const & root, map<Type*,uint> & type_map, map
 	}
 }
 
-void BuildUpBindPositions( const NiAVObjectRef & av ) {
+void BuildUpBindPositions( const NiAVObjectRef & root ) {
 
 	//Get parent if there is one
-	NiNodeRef par = av->GetParent();
+	NiNodeRef par = root->GetParent();
 	if ( par != NULL ) {
 		//There is a node parent
 
 		//Post-multipy the block's bind matrix with the parent's bind matrix
-		Matrix44 result = av->GetWorldBindPos() * par->GetWorldBindPos();
+		Matrix44 result = root->GetWorldBindPos() * par->GetWorldBindPos();
 
 		//Store result back to block bind position
-		av->SetWorldBindPos( result );
+		root->SetWorldBindPos( result );
 	}
 
-	//TODO:  Implement Child storage and access functions in NiNode
-	////Call this function for all child nodes if any
-	//attr_ref child_attr = block["Children"];
-	//if ( child_attr.is_null() == false ) {
-	//	list<NiObjectRef> children = child_attr->asLinkList();
-	//	list<NiObjectRef>::iterator it;
-	//	for (it = children.begin(); it != children.end(); ++it) {
-	//		BuildUpBindPositions( *it );
-	//	}
-	//}
+	//If this is a NiNode, call this function for all child AVObjects
+	NiNodeRef node = DynamicCast<NiNode>(root);
+	if ( node != NULL ) {
+		vector<NiAVObjectRef> children = node->GetChildren();
+		for (vector<NiAVObjectRef>::iterator it = children.begin(); it != children.end(); ++it) {
+			if ( *it != NULL ) {
+				BuildUpBindPositions( *it );
+			}
+		}
+	}
 }
 
 //TODO: Should this be returning an object of a derived type too?
diff --git a/obj/NiAVObject.cpp b/obj/NiAVObject.cpp
index 62ab0c8d..4e790b29 100644
--- a/obj/NiAVObject.cpp
+++ b/obj/NiAVObject.cpp
@@ -12,7 +12,10 @@ const Type NiAVObject::TYPE("NiAVObject", &NI_A_V_OBJECT_PARENT::TYPE );
 
 NiAVObject::NiAVObject() NI_A_V_OBJECT_CONSTRUCT, parent(NULL) {}
 
-NiAVObject::~NiAVObject() {}
+NiAVObject::~NiAVObject() {
+	//Clear Properties
+	ClearProperties();
+}
 
 void NiAVObject::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
 	NI_A_V_OBJECT_READ
@@ -93,7 +96,7 @@ Matrix44 NiAVObject::GetLocalBindPos() const {
  */
 void NiAVObject::SetWorldBindPos( Matrix44 const & m )  {}
 
-void NiAVObject::SetParent( Ref<NiNode> new_parent ) {
+void NiAVObject::SetParent( NiNode * new_parent ) {
 	parent = new_parent;
 }
 
diff --git a/obj/NiAVObject.h b/obj/NiAVObject.h
index 08f8fe6f..6d4214e3 100644
--- a/obj/NiAVObject.h
+++ b/obj/NiAVObject.h
@@ -36,7 +36,6 @@ public:
 	virtual void FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version );
 	virtual list<NiObjectRef> GetRefs() const;
 
-	//TODO: list of NiProperty pointers.  Need functions to add/remove.
 	//TODO:  Bounding Box.  What to do with newer files that have a link?  Wrap this in a function and translate?
 	
 	/*! 
@@ -74,7 +73,7 @@ public:
 	void SetWorldBindPos( Matrix44 const & m );
 
 	/*! Meant to be called by NiNode during the addition of new children.  Should not be called directly. */
-	void SetParent( Ref<NiNode> new_parent );
+	void SetParent( NiNode * new_parent );
 
 	Ref<NiNode> GetParent() const;
 
diff --git a/obj/NiNode.cpp b/obj/NiNode.cpp
index 69645d24..e98f6134 100644
--- a/obj/NiNode.cpp
+++ b/obj/NiNode.cpp
@@ -10,7 +10,10 @@ const Type NiNode::TYPE("NiNode", &NI_NODE_PARENT::TYPE );
 
 NiNode::NiNode() NI_NODE_CONSTRUCT {}
 
-NiNode::~NiNode() {}
+NiNode::~NiNode() {
+	//Clear Children
+	ClearChildren();
+}
 
 void NiNode::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
 	NI_NODE_READ
@@ -36,3 +39,33 @@ const Type & NiNode::GetType() const {
 	return TYPE;
 };
 
+void NiNode::AddChild( Ref<NiAVObject> & obj ) {
+	if ( obj->GetParent() != NULL ) {
+		throw runtime_error( "You have attempted to add a child to a NiNode which already is the child of another NiNode." );
+	}
+	obj->SetParent( this );
+	children.push_back( obj );
+}
+
+void NiNode::RemoveChild( Ref<NiAVObject> obj ) {
+	//Search child list for the one to remove
+	for ( vector< NiAVObjectRef >::iterator it = children.begin(); it != children.end(); ) {
+		if ( *it == obj ) {
+			(*it)->SetParent(NULL);
+			it = children.erase( it );
+		} else {
+			++it;
+		}
+	}
+}
+
+void NiNode::ClearChildren() {
+	for ( vector< NiAVObjectRef >::iterator it = children.begin(); it != children.end(); ) {
+		(*it)->SetParent(NULL);
+	}
+	children.clear();
+}
+
+vector< Ref<NiAVObject> > NiNode::GetChildren() const {
+	return children;
+}
diff --git a/obj/NiNode.h b/obj/NiNode.h
index 57f80936..7fee411f 100644
--- a/obj/NiNode.h
+++ b/obj/NiNode.h
@@ -14,6 +14,7 @@ class NiDynamicEffect;
 #include "../gen/obj_defines.h"
 
 class NiNode;
+class NiAVObject;
 typedef Ref<NiNode> NiNodeRef;
 
 /*!
@@ -32,6 +33,11 @@ public:
 	virtual void FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version );
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
+
+	void AddChild( Ref<NiAVObject> & obj );
+	void RemoveChild( Ref<NiAVObject> obj );
+	void ClearChildren();
+	vector< Ref<NiAVObject> > GetChildren() const;
 protected:
 	NI_NODE_MEMBERS
 };
diff --git a/obj/NiObjectNET.cpp b/obj/NiObjectNET.cpp
index e40044b8..7efe516f 100644
--- a/obj/NiObjectNET.cpp
+++ b/obj/NiObjectNET.cpp
@@ -10,7 +10,11 @@ const Type NiObjectNET::TYPE("NiObjectNET", &NI_OBJECT_N_E_T_PARENT::TYPE );
 
 NiObjectNET::NiObjectNET() NI_OBJECT_N_E_T_CONSTRUCT {}
 
-NiObjectNET::~NiObjectNET() {}
+NiObjectNET::~NiObjectNET() {
+	//Clear Lists
+	ClearExtraData();
+	ClearControllers();
+}
 
 void NiObjectNET::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
 	NI_OBJECT_N_E_T_READ
@@ -129,6 +133,7 @@ list< Ref<NiExtraData> > NiObjectNET::GetExtraData() const {
 
 void NiObjectNET::AddController( Ref<NiTimeController> & obj ) {
 	//Insert at begining of list
+	obj->SetTarget( this );
 	obj->SetNextController( controller );
 	controller = obj;
 }
@@ -138,6 +143,7 @@ void NiObjectNET::RemoveController( Ref<NiTimeController> obj ) {
 	while ( (*cont) != NULL ) {
 		if ( (*cont) == obj ) {
 			//Cut this reference out of the list
+			(*cont)->SetTarget( NULL );
 			(*cont) = (*cont)->GetNextController();
 		} else {
 			//Advance to the next controller
@@ -146,6 +152,10 @@ void NiObjectNET::RemoveController( Ref<NiTimeController> obj ) {
 	}
 }
 void NiObjectNET::ClearControllers() {
+	NiTimeControllerRef cont = controller;
+	while ( cont != NULL ) {
+		cont->SetTarget(NULL);
+	}
 	controller = NULL;
 }
 
diff --git a/obj/NiTimeController.cpp b/obj/NiTimeController.cpp
index 9fa87f72..f2addda8 100644
--- a/obj/NiTimeController.cpp
+++ b/obj/NiTimeController.cpp
@@ -2,7 +2,7 @@
 All rights reserved.  Please see niflib.h for licence. */
 
 #include "NiTimeController.h"
-#include "NiObject.h"
+#include "NiObjectNET.h"
 
 //Definition of TYPE constant
 const Type NiTimeController::TYPE("NiTimeController", &NI_TIME_CONTROLLER_PARENT::TYPE );
@@ -42,3 +42,11 @@ NiTimeControllerRef NiTimeController::GetNextController() const {
 void NiTimeController::SetNextController( const NiTimeControllerRef & obj ) {
 	nextController = obj;
 }
+
+void NiTimeController::SetTarget( NiObjectNET * new_target ) {
+	target = new_target;
+}
+
+Ref<NiObjectNET> NiTimeController::GetTarget() {
+	return target;
+}
diff --git a/obj/NiTimeController.h b/obj/NiTimeController.h
index 3576dd3a..0af01fb0 100644
--- a/obj/NiTimeController.h
+++ b/obj/NiTimeController.h
@@ -13,6 +13,7 @@ class NiObject;
 #include "../gen/obj_defines.h"
 
 class NiTimeController;
+class NiObjectNET;
 typedef Ref<NiTimeController> NiTimeControllerRef;
 
 /*!
@@ -43,6 +44,17 @@ public:
 	 * \param obj A reference to the object to set as the one after this in the chain.
 	 */
 	void SetNextController( const NiTimeControllerRef & obj );
+
+	/*! This function should only be called by NiObjectNET.  It sets the target of
+	 * this controller when it is attatched to the NiObjectNET class. */
+	void SetTarget( NiObjectNET * new_target );
+
+	/*! This function returns the current target NiObjectNET, if any, that this controller
+	 * is acting on.
+	 * \return A reference to the current target of this controller.
+	 */
+	Ref<NiObjectNET> GetTarget();
+
 protected:
 	NI_TIME_CONTROLLER_MEMBERS
 };
-- 
GitLab