/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "scene3d/scene.h" #include "scene3d/mobject.h" #include "scene3d/mlight.h" #include "scene3d/mcamera.h" #include "scene3d/mtrigger.h" #include "scene3d/instance.h" #include "scene3d/memitter.h" #include "scene3d/mterrain.h" #include "scene3d/mconstraint.h" #include "scene3d/group.h" #include "physic/physic_constraint.h" #include "physic/physic_world.h" #include "ui/ui.h" #include "script/scripted_object.h" #include "script/script_unit.h" #include "container/nmap.h" using namespace GS; using namespace S3D; using NML::Tag; //------------------------------------------------------------------------------ static MItem *GetMappedItemFromTag(Scene &scene, Tag *pt, const char *tag, Map &uid_map, MItemType type = Type_None) { Tag *muid = pt->GetTypedTag(tag, Variant::VariantInteger); if (!muid) __ERR__(__LOG_W__ << "Invalid UID tag.\n", NULL) uint uid = muid->GetUnsigned(); if (!uid_map.HasKey(uid)) __ERR__(__LOG_W__ << "Invalid item UID.\n", NULL) MItem *mitem = scene.ItemFromUid(uid_map[uid]); if (mitem && (type != Type_None) && (mitem->GetItemType() != type)) __ERR__(__LOG_V__ << "Item type filtered out.\n", NULL) return mitem; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static bool ShouldLoadItem(ToolMode mode, Tag *pt, uint flag, int load_level) { if (pt->GetTag("MItem:ToolSpecific;") || pt->GetTag("MItem:Helper;")) { if (!(flag & SceneIOHelper)) return false; // Do not load tool specific items in no tool or project preview modes. if ((mode == NoTool) || (mode == ToolProjectPreview)) return false; // Do not load tool specific items in no tool mode above the first load level. if (load_level > 0) return false; } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ template static T *LoadItemDerived(Scene *scene, T *i, Map &uid_map, Tag *t, uint type_flag, const uint load_flag, int load_level, ToolMode tool_mode) { if (!(load_flag & type_flag) || !ShouldLoadItem(tool_mode, t, load_flag, load_level)) { delete i; return NULL; } scene->SetupItemComponents(i); if (i->FromMetaTag(*t)) { uint olduid = i->GetUid(); // uid from source scene scene->AddItem(i, false); // add to live scene uid_map.Add(olduid, i->GetUid()); // store map from source to live scene } return i; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Scene::FromMetaTagStoreGroup(const Tag &tag, Group **group, uint load_flag, ToolMode tool_mode, int load_level) { if (tag.name != "Scene") __ERR__(__LOG_E__ << "Could not parse scene, incorrect root tag (" << tag.name << ").\n", false); Benchmark bench(true); g_scene_messenger.BroadcastMessage(SceneMsg_Loading, this, 0); ui->ResetCommandList(); // Create scene group. if (group && !group[0]) { group[0] = new Group; group[0]->name = "SceneGroup"; group_list.Add(group[0]); } // Read items and setup uid remap array. Map uid_map; if (Tag *itag = tag.GetTag("Items")) { // Item count in scene. uint item_to_load = 0; NMLTagForeach(pt, *itag) if (Tag *item_tag = pt->GetTag("MItem;")) item_to_load++; NMLTagForeach(pt, *itag) { MItem *citem = NULL; if (pt->name == "MCamera") citem = LoadItemDerived(this, new MCamera, uid_map, pt, SceneIOCamera, load_flag, load_level, tool_mode); else if (pt->name == "MObject") citem = LoadItemDerived(this, new MObject, uid_map, pt, SceneIOObject, load_flag, load_level, tool_mode); else if (pt->name == "MLight") citem = LoadItemDerived(this, new MLight, uid_map, pt, SceneIOLight, load_flag, load_level, tool_mode); else if ((pt->name == "MTrigger") || (pt->name == "Trigger")) // Legacy citem = LoadItemDerived(this, new MTrigger, uid_map, pt, SceneIOTrigger, load_flag, load_level, tool_mode); else if (pt->name == "Instance") citem = LoadItemDerived(this, new Instance, uid_map, pt, SceneIOInstance, load_flag, load_level, tool_mode); else if (pt->name == "MEmitter") citem = LoadItemDerived(this, new MEmitter, uid_map, pt, SceneIOEmitter, load_flag, load_level, tool_mode); else if (pt->name == "MTerrain") citem = LoadItemDerived(this, new MTerrain, uid_map, pt, SceneIOTerrain, load_flag, load_level, tool_mode); else if (pt->name == "MConstraint") citem = LoadItemDerived(this, new MConstraint(physic_world ? physic_world->NewConstraint() : NULL), uid_map, pt, SceneIOConstraint, load_flag, load_level, tool_mode); else __LOG_W__ << "Unexpected <" << pt->name << "> sub-tag in .\n"; if (citem) { // Add item to the scene group. if (group && group[0]) group[0]->Add(citem); // Remove from active list if inactive. if (!citem->isActive()) active_list.Remove(citem); } } } // Solve links. if (Tag *ltag = tag.GetTag("Links")) { NMLTagForeach(pt, *ltag) { if (pt->name == "Link") { int item = -1, link = -1; NMLTagForeach(lt, *pt) { if (lt->name == "Item") item = lt->GetInteger(); else if (lt->name == "Link") link = lt->GetInteger(); } MItem *sitem = NULL, *litem = NULL; if ((item == -1) || (link == -1)) __LOG_W__ << "Incomplete link tag.\n"; else { // Some items might have been masked out from import. if (uid_map.HasKey(item) && uid_map.HasKey(link)) { sitem = ItemFromUid(uid_map[item]); litem = ItemFromUid(uid_map[link]); if (sitem && litem) sitem->GetBaseItem()->SetParent(litem->GetBaseItem()); // __LOG__ << "Solving link: " << item << "(" << (sitem ? "OK" : "Failed") << ")->" << link << "(" << (litem ? "OK" : "Failed") << ").\n"; } // else __LOG_W__ << "Link item masked out during import.\n"; } } else __LOG_W__ << "Unexpected <" << pt->name << "> sub-tag in .\n"; } } // Solve constraints. if (Tag *ctag = tag.GetTag("Constraints")) { NMLTagForeach(pt, *ctag) if (pt->name == "Constraint") { MItem *mitem = GetMappedItemFromTag(*this, pt, "Uid;", uid_map, Type_Constraint); if (!mitem) continue; // Get constraint. if (MConstraint *constraint = (MConstraint *)mitem) { MItem *mitem_a = GetMappedItemFromTag(*this, pt, "UidA;", uid_map), *mitem_b = GetMappedItemFromTag(*this, pt, "UidB;", uid_map); constraint->desc.item_a = mitem_a; constraint->desc.item_b = mitem_b; } } } // Solve skins. if (Tag *btag = tag.GetTag("Skins")) { NMLTagForeach(pt, *btag) if (pt->name == "Skin") if (MItem *mitem = GetMappedItemFromTag(*this, pt, "UId;", uid_map, Type_Object)) { // We can now safely cast to the managed and core objects. MObject *mobj = (MObject *)mitem; Core::Object *obj = (Core::Object *)mobj; uint bone_count = 0; NMLTagForeach(st, *pt) if (st->name == "Bone") bone_count++; if (!obj->AllocateSkin(bone_count)) continue; // Process skin binding. NMLTagForeach(st, *pt) if (st->name == "Bone") { // Grab mandatory bone tags. Tag *itag = st->GetTypedTag("Index", Variant::VariantInteger), *utag = st->GetTypedTag("UId", Variant::VariantInteger); if (!itag || !utag) { __LOG_E__ << "Invalid bone information, incomplete bone tag.\n"; continue; } int index = itag->GetInteger(); if ((index < 0) || (index >= (int)obj->GetBoneCount())) { __LOG_E__ << "Invalid bone information, bone index out of bound.\n"; continue; } // Locate bone item. uint uid = utag->GetUnsigned(); if (!uid_map.HasKey(uid)) { __LOG_E__ << "Invalid bone information, item uid out of bound.\n"; continue; } MItem *bone = ItemFromUid(uid_map[uid]); if (!bone) { __LOG_E__ << "Invalid bone information, item not found.\n"; continue; } obj->BindBone(index, bone->GetBaseItem()); } } } // Read groups. if (load_flag & SceneIOGroup) if (Tag *gstag = tag.GetTag("Groups")) { String _item("Item"); NMLTagForeach(gtag, *gstag) { if (gtag->name == "Group") { if (Tag *tid = gtag->GetTag("Id")) { if (Group *new_group = new Group) { new_group->name = tid->GetString(); group_list.Add(new_group); NMLTagForeach(mtag, *gtag) { if (mtag->name == _item) { int uid = mtag->GetInteger(); if (uid_map.HasKey(uid)) new_group->Add(ItemFromUid(uid_map[uid])); // else // __LOG_W__ << "Group member masked out during import.\n"; } } // Add as sub-group. if (group) group[0]->Add(new_group); } else __LOG_E__ << "Failed to allocate group '" << tid->GetString() << "'.\n"; } else __LOG_W__ << "Could not read group, no identifier found.\n"; } } } // Script unit. if (load_flag & SceneIOScript) { if (Tag *stag = tag.GetTag("ScriptedObject")) scripted_object->FromMetaTag(*stag); #if 1 // Compatibility else if (Tag *stag = tag.GetTag("ScriptUnit")) if (Script::Unit *unit = scripted_object->AddUnit(scripted_object->NewUnit())) unit->FromMetaTag(*stag); #endif } // Motion sets. if (load_flag & SceneIOMotion) if (Tag *ctag = tag.GetTag("SceneMotionContainer")) { if (group) { if (*group) (*group)->motion.FromMetaTag(*ctag, &uid_map); } else motion.FromMetaTag(*ctag, &uid_map); } // Scene properties. static String _bgc("BackgroundColor"), _abc("AmbientColor"), _abi("AmbientIntensity"), _tge("TargetExposure"), _feb("FogEnable"), _fgn("FogNear"), _fgf("FogFar"), _fgc("FogColor"), _rad("Radiance"), _ird("Irradiance"); if (!group || (load_flag & SceneIOGlobals)) if (Tag *gtag = tag.GetTag("Globals")) NMLTagForeach(t, *gtag) { if (t->name == _bgc) background_color.FromMetaTag(*t); else if (t->name == _abc) ambient_color.FromMetaTag(*t); else if (t->name == _abi) ambient_intensity = t->GetReal(); else if (t->name == _rad) radiance_probe = t->GetString(); else if (t->name == _ird) irradiance_probe = t->GetString(); else if (t->name == _tge) target_exposure = t->GetReal(); else if (t->name == _fgn) fog_near = t->GetReal(); else if (t->name == _fgf) fog_far = t->GetReal(); else if (t->name == _fgc) fog_color.FromMetaTag(*t); else if (t->name == "SkyLayer0") skybox_layer[0] = t->GetString(); else if (t->name == "SkyLayer1") skybox_layer[1] = t->GetString(); else if (t->name == "SkyShader") skybox_shader = t->GetString(); else if (t->name == "TimeOfDay") time_of_day = t->GetReal(); } // Physic system. if ((load_flag & SceneIOPhysic) && physic_world) { Tag *ptag = tag.GetTag("Physics;"); if (!ptag) ptag = tag.GetTag("Tau;"); // Look for a legacy tag. if (ptag) { NMLTagForeach(t, *ptag) if (t->name == "Frequency") physic_world->SetTimestep(1.f / t->GetReal()); } } // Group stats. if (group && group[0]) __LOG__ << "Group '" << group[0]->name << "': " << group[0]->GetItemList().GetCount() << " item(s).\n"; // Load current camera. if ((load_flag & SceneIOSettings) && (load_flag & SceneIOCamera)) if (Tag *cctag = tag.GetTag("CurrentCamera")) { MItem *i = Item(cctag->GetString()); if (i && (i->GetItemType() == Type_Camera)) current_camera = (Camera *)i->GetBaseItem(); } // Query the editor tag if no camera found. if (!current_camera && (tool_mode != ToolEdit)) if (MCamera *camera = new MCamera) { camera->name = "Default"; AddItem(camera, true); current_camera = camera; if (Tag *pt = tag.GetTag("ViewMatrix;")) { Matrix4 m; if (m.FromMetaTag(*pt)) camera->SetMatrix(m); } } bench.Stop(); __LOG__ << "Done loading scene in " << bench.GetMs() << "ms.\n"; g_scene_messenger.BroadcastMessage(SceneMsg_Loaded, this, 0); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Scene::FromMetaFileStoreGroup(const char *path, Group **group, uint flag, ToolMode tool_mode, int load_level) { NML::File file; if (!NML::Parser::Load(path, file)) return false; if (Tag *scene_tag = file.GetTag("Scene")) { if (FromMetaTagStoreGroup(*scene_tag, group, flag, tool_mode, load_level)) name = path; } else __ERR__(__LOG_E__ << "No tag in '" << path << "'.\n", false) if (group && group[0]) group[0]->name = path; return true; } bool Scene::IsItemToBeSaved(MItem *i, const Group *group, uint flag) const { if (!i) return false; // Filter out irrelevant items. if (group && !group->IsMember(i)) return false; switch (i->GetItemType()) { case Type_Camera: if (!(flag & SceneIOCamera)) return false; break; case Type_Object: if (!(flag & SceneIOObject)) return false; break; case Type_Light: if (!(flag & SceneIOLight)) return false; break; case Type_Trigger: if (!(flag & SceneIOTrigger)) return false; break; case Type_Instance: if (!(flag & SceneIOInstance)) return false; break; case Type_Emitter: if (!(flag & SceneIOEmitter)) return false; break; case Type_Terrain: if (!(flag & SceneIOTerrain)) return false; break; default: break; } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Tag *Scene::LinkInfoAsMetaTag(MItem *i, const Group *group, uint flag) const { if (!IsItemToBeSaved(i, group, flag)) return NULL; // Grab link managed item. Core::Item *link = i->GetBaseItem() ? i->GetBaseItem()->GetParent() : NULL; MItem *mlink; if (!link || ((mlink = LocateManagedItem(link)) == NULL)) return NULL; if (!IsItemToBeSaved(mlink, group, flag)) return NULL; // Save link information. if (Tag *tag = new Tag("Link")) { tag->AddChild("Item", i->GetUid()); tag->AddChild("Link", mlink->GetUid()); return tag; } __ERR__(__LOG_E__ << "Failed to allocate link tag.\n", NULL) } Tag *Scene::SkinInfoAsMetaTag(MObject *mobj, const Group *group, uint flag) const { if (group && (!group->IsMember(mobj))) return NULL; // Go to the low-level core object. Core::Object *obj = (Core::Object *)mobj; if (!obj->HasSkin()) return NULL; Tag *skin_tag = new Tag("Skin"); if (!skin_tag) __ERR__(__LOG_E__ << "Failed to allocate skin tag.\n", NULL); skin_tag->AddChild("UId", mobj->GetUid()); for (int n = 0; n < (int)obj->GetBoneCount(); ++n) { MItem *bone = Scene::LocateManagedItem(obj->GetBone(n)); if (!bone || (group && (!group->IsMember(bone)))) continue; // Export bone informations. if (Tag *bone_tag = skin_tag->AddChild("Bone")) { bone_tag->AddChild("Index", n); bone_tag->AddChild("UId", bone->GetUid()); Matrix4 m; if (obj->GetBindMatrix(n, m)) bone_tag->AddChild(m.AsMetaTag("BindMatrix")); else __LOG_E__ << "Failed to serialize bone " << n << " bind matrix for object '" << mobj->name << "'.\n"; } else __LOG_E__ << "Failed to allocate bone tag.\n"; } return skin_tag; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Tag *Scene::AsMetaTag(const Group *group, uint save_flag, ToolMode tool_mode) const { Tag *root = new Tag("Scene"); if (!root) __ERR__(__LOG_E__ << "Could not create scene root tag to serialize.\n", NULL); // Write script object. if (scripted_object.IsValid() && !group) root->AddChild(scripted_object->AsMetaTag()); // Write motion sets. if (save_flag & SceneIOMotion) root->AddChild(motion.AsMetaTag()); // Write managed items. if (Tag *itag = root->AddChild("Items")) ListForeachPtr(MItem *, i, item_list) { if (group && !group->IsMember(i)) continue; switch (i->GetItemType()) { case Type_Camera: if (!(save_flag & SceneIOCamera)) continue; break; case Type_Object: if (!(save_flag & SceneIOObject)) continue; break; case Type_Light: if (!(save_flag & SceneIOLight)) continue; break; case Type_Trigger: if (!(save_flag & SceneIOTrigger)) continue; break; case Type_Instance: if (!(save_flag & SceneIOInstance)) continue; break; case Type_Emitter: if (!(save_flag & SceneIOEmitter)) continue; break; case Type_Terrain: if (!(save_flag & SceneIOTerrain)) continue; break; case Type_Constraint: if (!(save_flag & SceneIOConstraint)) continue; break; default: break; } itag->AddChild(i->AsMetaTag()); } else __ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL); // Write link informations. if (Tag *ltag = root->AddChild("Links")) ListForeachPtr(MItem *, i, item_list) ltag->AddChild(LinkInfoAsMetaTag(i, group, save_flag)); else __ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL) // Write skin informations. if (Tag *stag = root->AddChild("Skins")) { ListForeachPtr(MItem *, i, item_list) if (i->GetItemType() == Type_Object) stag->AddChild(SkinInfoAsMetaTag((MObject *)i, group, save_flag)); if (stag->GetChildCount() == 0) { root->RemoveTag(stag); _safe_delete(stag); } } else __ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL); // Write constraints. if (Tag *constraint_tag = root->AddChild("Constraints")) ListForeachPtr(MItem *, i, item_list) if (i->GetItemType() == Type_Constraint) { if (group && !group->IsMember(i)) continue; MConstraint *c = (MConstraint *)i; if (Tag *ctag = constraint_tag->AddChild("Constraint")) { ctag->AddChild("Uid", c->GetUid()); if (c->desc.item_a.IsValid()) ctag->AddChild("UidA", c->desc.item_a->GetUid()); if (c->desc.item_b.IsValid()) ctag->AddChild("UidB", c->desc.item_b->GetUid()); } } // Write scene groups. if (!group) { if ((save_flag & SceneIOGroup) && group_list.GetCount()) { if (Tag *gstag = root->AddChild("Groups")) { ListForeachPtr(Group *, group, group_list) if (Tag *gtag = gstag->AddChild("Group")) { gtag->AddChild("Id", group->name.c_str()); ListForeachPtr(MItem *, item, group->GetItemList()) gtag->AddChild("Item", item->GetUid()); } } else __ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL); } // Output scene properties. if (save_flag & SceneIOGlobals) { if (Tag *gtag = root->AddChild("Globals")) { gtag->AddChild(background_color.AsMetaTag("BackgroundColor")); gtag->AddChild(ambient_color.AsMetaTag("AmbientColor")); if (!irradiance_probe.IsEmpty()) gtag->AddChild("Irradiance", irradiance_probe.c_str()); if (!radiance_probe.IsEmpty()) gtag->AddChild("Radiance", radiance_probe.c_str()); if (!skybox_layer[0].IsEmpty()) gtag->AddChild("SkyLayer0", skybox_layer[0].c_str()); if (!skybox_layer[1].IsEmpty()) gtag->AddChild("SkyLayer1", skybox_layer[1].c_str()); if (!skybox_shader.IsEmpty()) gtag->AddChild("SkyShader", skybox_shader.c_str()); gtag->AddChild("TimeOfDay", time_of_day); gtag->AddChild("AmbientIntensity", ambient_intensity); gtag->AddChild("TargetExposure", target_exposure); gtag->AddChild("FogNear", fog_near); gtag->AddChild("FogFar", fog_far); gtag->AddChild(fog_color.AsMetaTag("FogColor")); } else __ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL); } // Physics. if ((save_flag & SceneIOPhysic) && physic_world.IsValid()) if (Tag *ptag = root->AddChild("Physics")) ptag->AddChild("Frequency", 1.f / physic_world->GetTimestep()); // Current camera. if (current_camera) if (MItem *ci = LocateManagedItem(current_camera)) root->AddChild("CurrentCamera", ci->name); } return root; } //------------------------------------------------------------------------------