// IAEngine: 2D Game Engine by IA // Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) // // 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 3 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, see . #include #include #include namespace ia::iae { // ----------------------------------------------- // getInnerXML and xml_string_writer were taken from https://stackoverflow.com/a/60337372 struct xml_string_writer : pugi::xml_writer { std::string result; virtual void write(const void *data, size_t size) { result.append(static_cast(data), size); } }; const auto getInnerXML = [](pugi::xml_node target) { xml_string_writer writer; for (pugi::xml_node child = target.first_child(); child; child = child.next_sibling()) child.print(writer, ""); return writer.result; }; // ----------------------------------------------- RefPtr Scene::Create() { const auto scene = MakeRefPtr(); scene->Extent() = Engine::GetDisplayExtent(); const auto cameraNode = MakeRefPtr("MainCamera"); scene->AddNode(cameraNode); scene->m_camera = cameraNode->GetCameraComponent(); return scene; } RefPtr Scene::Create(IN CONST String &sceneXML, IN std::function(IN CONST String &, IN Handle)> getCustomNode, IN std::function getResource) { const auto scene = Create(); pugi::xml_document doc; doc.load_string(sceneXML.c_str()); const auto sceneRoot = doc.child("Scene"); Map resources; // Process Resources const auto resRoot = sceneRoot.child("Resources"); if (!resRoot) THROW_INVALID_DATA("Scene file is missing 'Resources' tag"); { for (const auto &t : resRoot.children()) { if (!strcmp(t.name(), "Image")) { resources[t.attribute("name").as_string()] = getResource( ResourceType::IMAGE, t.attribute("path").as_string(), t.attribute("index").as_llong()); } else if (!strcmp(t.name(), "Audio")) { resources[t.attribute("name").as_string()] = getResource(ResourceType::SOUND, t.attribute("path").as_string(), 0); } } } // Process Properties const auto propRoot = sceneRoot.child("Properties"); if (!propRoot) THROW_INVALID_DATA("Scene file is missing 'Properties' tag"); { auto t = propRoot.child("Extent"); scene->Extent() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; t = propRoot.child("Viewport"); scene->Viewport() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; } // Process Nodes const auto nodeRoot = sceneRoot.child("Nodes"); if (!nodeRoot) THROW_INVALID_DATA("Scene file is missing 'Nodes' tag"); { for (const auto &t : nodeRoot) { Node2D* n{}; if (!strcmp(t.name(), "TextureNode")) { const auto node = MakeRefPtr(Engine::GetUniqueResourceName()); node->GetTextureComponent()->SetTexture(resources[t.attribute("texture").as_string()]); scene->AddNode(node); n = node.get(); } else { Handle id{INVALID_HANDLE}; const auto attrID = t.attribute("id"); if(attrID) id = (Handle)attrID.as_ullong(); const auto node = getCustomNode(t.name(), id); scene->AddNode(node); n = node.get(); } if(!n) continue; if(t.attribute("X") && t.attribute("Y")) { n->SetLocalPosition({ t.attribute("X").as_float(), t.attribute("Y").as_float() }); } } } // Process UI const auto uiRoot = sceneRoot.child("UI"); if (!uiRoot) THROW_INVALID_DATA("Scene file is missing 'UI' tag"); { scene->UIMarkupStyles() = getInnerXML(uiRoot.child("CSS")).c_str(); auto html = String(getInnerXML(uiRoot.child("HTML")).c_str()); html = Utils::RegexReplaceGroups(html, "UIMarkup() = html; } return scene; } VOID Scene::Draw() { for (auto &t : m_nodes) t->Value->Draw(); } VOID Scene::DebugDraw() { for (auto &t : m_nodes) t->Value->DebugDraw(); } VOID Scene::FixedUpdate() { for (auto &t : m_nodes) t->Value->FixedUpdate(); } VOID Scene::Update() { for (auto &t : m_nodes) t->Value->Update(); } VOID Scene::AddNode(IN RefPtr node) { m_nodes[node->GetName()] = node; } INode *Scene::GetNode(IN CONST String &name) { return m_nodes[name].get(); } VOID Scene::RemoveNode(IN CONST String &name) { m_nodes[name] = nullptr; } VOID Scene::OnActivate() { UI::SetMarkup(UIMarkup(), UIMarkupStyles()); if(m_camera) Engine::SetActiveCamera(m_camera); } } // namespace ia::iae