| 
									
										
										
										
											2011-02-23 10:52:22 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  |  * ***** BEGIN GPL LICENSE BLOCK ***** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  |  * of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Nathan Letwory. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * ***** END GPL LICENSE BLOCK ***** | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-27 20:30:35 +00:00
										 |  |  | /** \file blender/collada/collada_utils.cpp
 | 
					
						
							|  |  |  |  *  \ingroup collada | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-12 06:25:04 +00:00
										 |  |  | /* COLLADABU_ASSERT, may be able to remove later */ | 
					
						
							|  |  |  | #include "COLLADABUPlatform.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | #include "COLLADAFWGeometry.h"
 | 
					
						
							|  |  |  | #include "COLLADAFWMeshPrimitive.h"
 | 
					
						
							|  |  |  | #include "COLLADAFWMeshVertexData.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | #include "collada_utils.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | extern "C" { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | #include "DNA_modifier_types.h"
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | #include "DNA_customdata_types.h"
 | 
					
						
							|  |  |  | #include "DNA_object_types.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | #include "DNA_mesh_types.h"
 | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | #include "DNA_scene_types.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | #include "DNA_armature_types.h"
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_math.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BKE_context.h"
 | 
					
						
							|  |  |  | #include "BKE_customdata.h"
 | 
					
						
							|  |  |  | #include "BKE_depsgraph.h"
 | 
					
						
							|  |  |  | #include "BKE_object.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | #include "BKE_mesh.h"
 | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | #include "BKE_scene.h"
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | #include "BKE_DerivedMesh.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | #include "BLI_linklist.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | #include "WM_api.h" // XXX hrm, see if we can do without this
 | 
					
						
							|  |  |  | #include "WM_types.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (index >= array.getValuesCount()) | 
					
						
							|  |  |  | 		return 0.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) | 
					
						
							|  |  |  | 		return array.getFloatValues()->getData()[index]; | 
					
						
							|  |  |  | 	else  | 
					
						
							|  |  |  | 		return array.getDoubleValues()->getData()[index]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // copied from /editors/object/object_relations.c
 | 
					
						
							|  |  |  | int bc_test_parent_loop(Object *par, Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* test if 'ob' is a parent somewhere in par's parents */ | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2012-03-24 07:52:14 +00:00
										 |  |  | 	if (par == NULL) return 0; | 
					
						
							|  |  |  | 	if (ob == par) return 1; | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	return bc_test_parent_loop(par->parent, ob); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // a shortened version of parent_set_exec()
 | 
					
						
							|  |  |  | // if is_parent_space is true then ob->obmat will be multiplied by par->obmat before parenting
 | 
					
						
							|  |  |  | int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Object workob; | 
					
						
							|  |  |  | 	Main *bmain = CTX_data_main(C); | 
					
						
							|  |  |  | 	Scene *sce = CTX_data_scene(C); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (!par || bc_test_parent_loop(par, ob)) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ob->parent = par; | 
					
						
							|  |  |  | 	ob->partype = PAROBJECT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ob->parsubstr[0] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (is_parent_space) { | 
					
						
							|  |  |  | 		float mat[4][4]; | 
					
						
							|  |  |  | 		// calc par->obmat
 | 
					
						
							| 
									
										
										
										
											2012-05-05 14:03:12 +00:00
										 |  |  | 		BKE_object_where_is_calc(sce, par); | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// move child obmat into world space
 | 
					
						
							| 
									
										
										
											
												Math lib: matrix multiplication order fix for two functions that were
inconsistent with similar functions & math notation:
mul_m4_m4m4(R, B, A) => mult_m4_m4m4(R, A, B)
mul_m3_m3m4(R, B, A) => mult_m3_m3m4(R, A, B)
For branch maintainers, it should be relatively simple to fix things manually,
it's also possible run this script after merging to do automatic replacement:
http://www.pasteall.org/27459/python
											
										 
											2011-12-16 19:53:12 +00:00
										 |  |  | 		mult_m4_m4m4(mat, par->obmat, ob->obmat); | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 		copy_m4_m4(ob->obmat, mat); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// apply child obmat (i.e. decompose it into rot/loc/size)
 | 
					
						
							| 
									
										
										
										
											2012-05-05 14:03:12 +00:00
										 |  |  | 	BKE_object_apply_mat4(ob, ob->obmat, 0, 0); | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// compute parentinv
 | 
					
						
							| 
									
										
										
										
											2012-05-05 14:03:12 +00:00
										 |  |  | 	BKE_object_workob_calc_parent(sce, ob, &workob); | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 	invert_m4_m4(ob->parentinv, workob.obmat); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA; | 
					
						
							|  |  |  | 	par->recalc |= OB_RECALC_OB; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DAG_scene_sort(bmain, sce); | 
					
						
							|  |  |  | 	DAG_ids_flush_update(bmain, 0); | 
					
						
							| 
									
										
										
										
											2012-06-12 22:05:33 +00:00
										 |  |  | 	WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); | 
					
						
							| 
									
										
										
										
											2010-10-05 00:05:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | Object *bc_add_object(Scene *scene, int type, const char *name) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-05 14:03:12 +00:00
										 |  |  | 	Object *ob = BKE_object_add_only_object(type, name); | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-12 22:05:33 +00:00
										 |  |  | 	ob->data = BKE_object_obdata_add_from_type(type); | 
					
						
							|  |  |  | 	ob->lay = scene->lay; | 
					
						
							|  |  |  | 	ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME; | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-05 14:33:36 +00:00
										 |  |  | 	BKE_scene_base_select(scene, BKE_scene_base_add(scene, ob)); | 
					
						
							| 
									
										
										
										
											2012-05-03 21:32:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ob; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-16 23:35:53 +00:00
										 |  |  | Mesh *bc_to_mesh_apply_modifiers(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type) | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	Mesh *tmpmesh; | 
					
						
							|  |  |  | 	CustomDataMask mask = CD_MASK_MESH; | 
					
						
							| 
									
										
										
										
											2012-06-16 23:35:53 +00:00
										 |  |  | 	DerivedMesh *dm; | 
					
						
							|  |  |  | 	switch (export_mesh_type) { | 
					
						
							|  |  |  | 		case BC_MESH_TYPE_VIEW: { | 
					
						
							|  |  |  | 			dm = mesh_create_derived_view(scene, ob, mask); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		case BC_MESH_TYPE_RENDER: { | 
					
						
							|  |  |  | 			dm = mesh_create_derived_render(scene, ob, mask); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | 	tmpmesh             = BKE_mesh_add("ColladaMesh"); // name is not important here
 | 
					
						
							|  |  |  | 	DM_to_mesh(dm, tmpmesh, ob); | 
					
						
							|  |  |  | 	dm->release(dm); | 
					
						
							|  |  |  | 	return tmpmesh; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Object *bc_get_assigned_armature(Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Object *ob_arm = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { | 
					
						
							|  |  |  | 		ob_arm = ob->parent; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							| 
									
										
										
										
											2012-06-12 22:05:33 +00:00
										 |  |  | 		ModifierData *mod; | 
					
						
							|  |  |  | 		for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) { | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | 			if (mod->type == eModifierType_Armature) { | 
					
						
							| 
									
										
										
										
											2012-06-12 22:05:33 +00:00
										 |  |  | 				ob_arm = ((ArmatureModifierData *)mod)->object; | 
					
						
							| 
									
										
										
										
											2012-06-07 17:55:26 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ob_arm; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | // Returns the highest selected ancestor
 | 
					
						
							|  |  |  | // returns NULL if no ancestor is selected
 | 
					
						
							|  |  |  | // IMPORTANT: This function expects that
 | 
					
						
							|  |  |  | // all exported objects have set:
 | 
					
						
							|  |  |  | // ob->id.flag & LIB_DOIT
 | 
					
						
							|  |  |  | Object *bc_get_highest_selected_ancestor_or_self(LinkNode *export_set, Object *ob)  | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Object *ancestor = ob; | 
					
						
							|  |  |  | 	while (ob->parent && bc_is_marked(ob->parent)) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		ob = ob->parent; | 
					
						
							|  |  |  | 		ancestor = ob; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ancestor; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | bool bc_is_base_node(LinkNode *export_set, Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Object *root = bc_get_highest_selected_ancestor_or_self(export_set, ob); | 
					
						
							|  |  |  | 	return (root == ob); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool bc_is_in_Export_set(LinkNode *export_set, Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 	return (BLI_linklist_index(export_set, ob) != -1); | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool bc_has_object_type(LinkNode *export_set, short obtype) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 	LinkNode *node; | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 	for (node = export_set; node; node = node->next) { | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | 		Object *ob = (Object *)node->link; | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 		/* XXX - why is this checking for ob->data? - we could be looking for empties */ | 
					
						
							| 
									
										
										
										
											2012-06-12 21:25:29 +00:00
										 |  |  | 		if (ob->type == obtype && ob->data) { | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int bc_is_marked(Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ob && (ob->id.flag & LIB_DOIT); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void bc_remove_mark(Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ob->id.flag &= ~LIB_DOIT; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Use bubble sort algorithm for sorting the export set
 | 
					
						
							|  |  |  | void bc_bubble_sort_by_Object_name(LinkNode *export_set) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-14 14:48:52 +00:00
										 |  |  | 	bool sorted = false; | 
					
						
							|  |  |  | 	LinkNode *node; | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 	for (node = export_set; node->next && !sorted; node = node->next) { | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 14:48:52 +00:00
										 |  |  | 		sorted = true; | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2012-06-14 14:48:52 +00:00
										 |  |  | 		LinkNode *current; | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 		for (current = export_set; current->next; current = current->next) { | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 			Object *a = (Object *)current->link; | 
					
						
							|  |  |  | 			Object *b = (Object *)current->next->link; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 			if (strcmp(a->id.name, b->id.name) > 0) { | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 				current->link       = b; | 
					
						
							|  |  |  | 				current->next->link = a; | 
					
						
							| 
									
										
										
										
											2012-06-14 14:48:52 +00:00
										 |  |  | 				sorted = false; | 
					
						
							| 
									
										
										
										
											2012-06-14 10:38:39 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-06-15 02:21:07 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Check if a bone is the top most exportable bone in the bone hierarchy. 
 | 
					
						
							|  |  |  |  * When deform_bones_only == false, then only bones with NO parent  | 
					
						
							|  |  |  |  * can be root bones. Otherwise the top most deform bones in the hierarchy | 
					
						
							|  |  |  |  * are root bones. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-20 16:43:48 +00:00
										 |  |  | bool bc_is_root_bone(Bone *aBone, bool deform_bones_only) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 	if (deform_bones_only) { | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | 		Bone *root = NULL; | 
					
						
							|  |  |  | 		Bone *bone = aBone; | 
					
						
							|  |  |  | 		while (bone) { | 
					
						
							|  |  |  | 			if (!(bone->flag & BONE_NO_DEFORM)) | 
					
						
							|  |  |  | 				root = bone; | 
					
						
							|  |  |  | 			bone = bone->parent; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-06-16 09:16:24 +00:00
										 |  |  | 		return (aBone == root); | 
					
						
							| 
									
										
										
										
											2012-06-15 22:00:25 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return !(aBone->parent); | 
					
						
							|  |  |  | } |