/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "tools/scene_merge_object_list.h" #include "tools/geometry_merge.h" #include "scene3d/scene.h" #include "scene3d/mobject.h" #include "physic/physic_world.h" #include "core/graphic_resource_factory.h" using namespace GS; using namespace GS::Core; using namespace GS::S3D; //------------------------------------------------------------------------------ struct ItemTransform { sMItem i; Matrix4 m; ItemTransform(MItem *_i, const Matrix4 &_m) : i(_i), m(_m) {} }; struct GeometryTransform { sMItem i; Matrix4 m; sGeometry g; GeometryTransform(MItem *_i, Geometry *_g, const Matrix4 &_m) : i(_i), m(_m), g(_g) {} }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static void MergePhysicShape(MItem *to, MItem *from) { if (!from->physic_item) return; ListForeachPtr(PhysicShape *, shape, from->physic_item_desc.shape_list) if (PhysicShape *new_shape = new PhysicShape) { new_shape->SetMatrix(from->GetBaseItem()->GetMatrix() * shape->GetMatrix()); new_shape->mass = shape->mass; switch (shape->GetType()) { case PhysicShape::TypeNone: break; case PhysicShape::TypeHeightmap: __LOG_W__ << "Merge heightmap physic shape STUB.\n"; break; case PhysicShape::TypeBox: case PhysicShape::TypeCapsule: case PhysicShape::TypeCylinder: case PhysicShape::TypeCone: case PhysicShape::TypeSphere: new_shape->Set(shape->GetType(), shape->dimensions); break; case PhysicShape::TypeConvex: case PhysicShape::TypeMesh: new_shape->Set(shape->GetType(), shape->path); break; } to->physic_item_desc.shape_list.Add(new_shape); } to->physic_item_desc.physic_mode = from->physic_item_desc.physic_mode; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool SceneMergeObjectList::Merge(Scene *scene, const SharedList &item_list, Core::ResourceFactory &gf, sMItem &out_item, sGeometry &out_geo) { // Unlink all children, store matrix beforehand. AutoList child_list; ListForeachPtr(MItem *, item, item_list) ListForeachPtr(Item *, child, item->GetBaseItem()->GetChildren()) { MItem *cchild = Scene::LocateManagedItem(child); // Do not store item if it is going to be merged. if (!item_list.Find(cchild)) child_list.Add(new ItemTransform(cchild, cchild->GetBaseItem()->GetMatrix())); } Vector4 average_vec(0.0,0.0,0.0); int counter_average = 0; ListForeachPtr(MItem *, item, item_list) if (item->GetItemType() == Type_Object) { ++counter_average; average_vec += item->GetBaseItem()->GetPosition(); } average_vec /= counter_average; ListForeachPtr(MItem *, item, item_list) if (item->GetItemType() == Type_Object) item->GetBaseItem()->SetPosition(item->GetBaseItem()->GetPosition() - average_vec); // Build merge list. AutoList geo_list; ListForeachPtr(MItem *, item, item_list) if (item->GetItemType() == Type_Object) geo_list.Add(new GeometryTransform(item, gf.LoadGeometry(((MObject *)item)->geometry), item->GetBaseItem()->GetMatrix())); if (geo_list.GetCount() < 2) return true; // nothing to be done // Count total steps. int total_step_count = 0; for (int count = item_list.GetCount(); count > 1; count /= 2) total_step_count += count; // Merge list. AutoList tgt_list, *in_list = &geo_list, *out_list = &tgt_list; int step_count = 0; for (bool running = true; (in_list->GetCount() > 1) && running; ) { for (uint n = 0; (n < in_list->GetCount()) && running; n += 2) { if (n == (in_list->GetCount() - 1)) { GeometryTransform *t = (*in_list)[n]; out_list->Add(new GeometryTransform(t->i, t->g, t->m)); continue; // nothing to merge } // Merge geometries. GeometryTransform *left = (*in_list)[n], *right = (*in_list)[n + 1]; sGeometry merged_geometry(MergeGeometry(left->g, right->g, &left->m, &right->m)); if (merged_geometry.IsNull()) continue; // Merge physic shapes. MObject *merged_object = new MObject; scene->SetupItemComponents(merged_object); MergePhysicShape(merged_object, left->i); MergePhysicShape(merged_object, right->i); out_list->Add(new GeometryTransform(merged_object, merged_geometry, Matrix4::IdentityMatrix())); running = OnProgress(++step_count, total_step_count); } in_list->Clear(); AutoList *tmp_list = in_list; in_list = out_list; out_list = tmp_list; } if (in_list->GetCount() != 1) return false; // Remove merged items from scene. ListForeachPtr(MItem *, item, item_list) if (item->GetItemType() == Type_Object) scene->RemoveItem(item); // Store output... out_item = (*in_list)[0]->i; out_geo = (*in_list)[0]->g; out_item->GetBaseItem()->SetPosition(average_vec); out_item->name = "Merged Object"; scene->AddItem(out_item, false); // ...restore parenting. ListForeachPtr(ItemTransform *, t, child_list) { t->i->GetBaseItem()->SetParent(out_item->GetBaseItem()); t->i->GetBaseItem()->SnapshotTransformation(t->m); } child_list.Clear(); return true; } //------------------------------------------------------------------------------