7#include "components/Environment.hpp"
13#if defined(__cpp_lib_execution) && defined(__cpp_lib_parallel_algorithm)
15 #define VEX_USE_PARALLEL_EXECUTION
18#include <nlohmann/json.hpp>
33 m_addedObjects.clear();
38 if (!m_engine->getFileSystem()->file_exists(realPath)) {
39 log(LogLevel::ERROR,
"Could not open scene file: %s", realPath.c_str());
45 std::unique_ptr<VirtualFileSystem::FileData> fileData =
nullptr;
46 const int maxRetries = 10;
47 const int retryDelayMs = 100;
49 for (
int i = 0; i < maxRetries; ++i) {
50 fileData = m_engine->getFileSystem()->load_file(realPath);
51 if (fileData && !fileData->data.empty()) {
54 std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
57 if (!fileData || fileData->data.empty()) {
58 throw_error(
"Failed to read scene file after 1 second of retries. File is empty, missing, or permanently locked by the OS: " + realPath);
64 json = nlohmann::json::parse(fileData->data.begin(), fileData->data.end(),
nullptr,
true);
69 if (json.contains(
"environment") && json[
"environment"].contains(
"shading")) {
70 log(
"Loading shading settings from scene");
71 const auto& shading = json[
"environment"][
"shading"];
82 if (json.contains(
"environment") && json[
"environment"].contains(
"lighting")) {
83 log(
"Loading lighting settings from scene");
84 const auto& lighting = json[
"environment"][
"lighting"];
87 if (lighting.contains(
"ambientLight") && lighting[
"ambientLight"].is_array() && lighting[
"ambientLight"].size() >= 3) {
89 lighting[
"ambientLight"][0].get<float>(),
90 lighting[
"ambientLight"][1].get<float>(),
91 lighting[
"ambientLight"][2].get<float>()
95 if (lighting.contains(
"sunLight") && lighting[
"sunLight"].is_array() && lighting[
"sunLight"].size() >= 3) {
97 lighting[
"sunLight"][0].get<float>(),
98 lighting[
"sunLight"][1].get<float>(),
99 lighting[
"sunLight"][2].get<float>()
103 if (lighting.contains(
"sunDirection") && lighting[
"sunDirection"].is_array() && lighting[
"sunDirection"].size() >= 3) {
105 lighting[
"sunDirection"][0].get<float>(),
106 lighting[
"sunDirection"][1].get<float>(),
107 lighting[
"sunDirection"][2].get<float>()
111 if (lighting.contains(
"clearColor") && lighting[
"clearColor"].is_array() && lighting[
"clearColor"].size() >= 3) {
113 lighting[
"clearColor"][0].get<float>(),
114 lighting[
"clearColor"][1].get<float>(),
115 lighting[
"clearColor"][2].get<float>()
120 m_engine->setEnvironmentSettings(env);
122 auto objects = json[
"objects"];
123 if (!objects.is_array()) {
124 log(LogLevel::ERROR,
"Scene file must have 'objects' array");
128 std::unordered_map<std::string, GameObject*> objectDirectory;
129 std::vector<std::pair<GameObject*, std::string>> pendingParenting;
131 for (
const auto& obj : objects) {
132 std::string type = obj.value(
"type",
"");
133 std::string name = obj.value(
"name",
"");
134 if (type.empty() || name.empty()) {
135 log(LogLevel::ERROR,
"Object missing type or name");
139 m_creatingFromScene =
true;
142 log(LogLevel::ERROR,
"Failed to create GameObject of type '%s'", type.c_str());
146 objectDirectory[name] = gameObj;
148 auto components = obj[
"components"];
149 if (!components.is_array()) {
150 log(LogLevel::WARNING,
"Object '%s' has no components to load", name.c_str());
152 for (
const auto& comp : components) {
153 std::string compType = comp.value(
"type",
"");
154 if (compType.empty()) {
155 log(LogLevel::ERROR,
"Component missing type for object '%s'", name.c_str());
158 ComponentRegistry::getInstance().loadComponent(*gameObj, compType, comp);
162 std::string parentName = obj.value(
"parent",
"");
163 if (!parentName.empty()) {
164 pendingParenting.push_back({gameObj, parentName});
167 for (
auto& pair : pendingParenting) {
169 const std::string& parentName = pair.second;
171 auto it = objectDirectory.find(parentName);
172 if (it != objectDirectory.end()) {
174 if (parentObj->isValid()) {
179 log(LogLevel::WARNING,
"Parent '%s' not found", parentName.c_str());
182 }
catch (
const std::exception& e) {
183 log(LogLevel::ERROR,
"Failed to load scene: %s", m_path.c_str());
191 uint32_t size = m_objects.size();
192 size += m_addedObjects.size();
194 bool useParallel =
false;
195 #ifdef VEX_USE_PARALLEL_EXECUTION
201 #ifdef VEX_USE_PARALLEL_EXECUTION
202 std::for_each(std::execution::par, m_objects.begin(), m_objects.end(), [&](
auto& obj){
203 try{ obj->BeginPlay(); }
catch(
const std::exception& e){ handle_exception(e); }
205 std::for_each(std::execution::par, m_addedObjects.begin(), m_addedObjects.end(), [&](
auto& obj){
206 try{ obj->BeginPlay(); }
catch(
const std::exception& e){ handle_exception(e); }
210 for (
auto& obj : m_objects) {
211 try{ obj->BeginPlay(); }
catch(
const std::exception& e){
handle_exception(e); }
213 for (
auto& obj : m_addedObjects) {
214 try{ obj->BeginPlay(); }
catch(
const std::exception& e){
handle_exception(e); }
222 uint32_t size = m_objects.size();
223 size += m_addedObjects.size();
225 bool useParallel =
false;
226 #ifdef VEX_USE_PARALLEL_EXECUTION
232 #ifdef VEX_USE_PARALLEL_EXECUTION
233 std::for_each(std::execution::par, m_objects.begin(), m_objects.end(), [&](
auto& obj){
234 try{ obj->Update(deltaTime); }
catch(
const std::exception& e){ log(
"Error: %s", e.what()); }
236 std::for_each(std::execution::par, m_addedObjects.begin(), m_addedObjects.end(), [&](
auto& obj){
237 try{ obj->Update(deltaTime); }
catch(
const std::exception& e){ log(
"Error: %s", e.what()); }
241 for (
auto& obj : m_objects) {
242 try{ obj->Update(deltaTime); }
catch(
const std::exception& e){
log(
"Error: %s", e.what()); }
244 for (
auto& obj : m_addedObjects) {
245 try{ obj->Update(deltaTime); }
catch(
const std::exception& e){
log(
"Error: %s", e.what()); }
252 m_addedObjects.push_back(std::move(gameObject));
258 if(m_creatingFromScene){
259 m_objects.emplace_back(std::shared_ptr<GameObject>(obj));
261 m_addedObjects.emplace_back(std::shared_ptr<GameObject>(obj));
263 m_creatingFromScene =
false;
270 if (std::find(m_pendingDestruction.begin(), m_pendingDestruction.end(), obj) == m_pendingDestruction.end()) {
271 m_pendingDestruction.push_back(obj);
276 if (m_pendingDestruction.empty())
return;
279 m_engine->WaitForGpu();
282 for (
GameObject* obj : m_pendingDestruction) {
285 auto it = std::find_if(m_objects.begin(), m_objects.end(),
286 [obj](
const std::shared_ptr<GameObject>& ptr) { return ptr.get() == obj; });
288 if (it != m_objects.end()) {
291 auto itAdded = std::find_if(m_addedObjects.begin(), m_addedObjects.end(),
292 [obj](
const std::shared_ptr<GameObject>& ptr) { return ptr.get() == obj; });
294 if (itAdded != m_addedObjects.end()) {
295 m_addedObjects.erase(itAdded);
299 m_pendingDestruction.clear();
303 std::vector<GameObject*> returnVector;
304 for(
const auto& obj : m_objects){
306 returnVector.push_back(obj.get());
309 for(
const auto& obj : m_addedObjects){
311 returnVector.push_back(obj.get());
318 std::vector<GameObject*> returnVector;
319 for(
const auto& obj : m_objects){
320 if(obj->getObjectType() == classname){
321 returnVector.push_back(obj.get());
324 for(
const auto& obj : m_addedObjects){
325 if(obj->getObjectType() == classname){
326 returnVector.push_back(obj.get());
333 for(
const auto& obj : m_objects){
334 if(obj->GetEntity() == entity){
338 for(
const auto& obj : m_addedObjects){
339 if(obj->GetEntity() == entity){
347 nlohmann::json sceneJson;
349 const auto& env = m_engine->getEnvironmentSettings();
350 sceneJson[
"environment"][
"lighting"] = {
351 {
"ambientLightStrength", env.ambientLightStrength},
352 {
"ambientLight", {env.ambientLight.r, env.ambientLight.g, env.ambientLight.b}},
353 {
"sunLight", {env.sunLight.r, env.sunLight.g, env.sunLight.b}},
354 {
"sunDirection", {env.sunDirection.x, env.sunDirection.y, env.sunDirection.z}},
355 {
"clearColor", {env.clearColor.r, env.clearColor.g, env.clearColor.b}}
358 sceneJson[
"environment"][
"shading"] = {
359 {
"gouraud", env.gourardShading},
360 {
"passiveVertexJitter", env.passiveVertexJitter},
361 {
"vertexSnapping", env.vertexSnapping},
362 {
"affineTextureWarping", env.affineWarping},
363 {
"screenQuantization", env.screenQuantization},
364 {
"textureQuantization", env.textureQuantization},
365 {
"screenDither", env.screenDither},
366 {
"ntfsArtifacts", env.ntfsArtifacts}
369 std::unordered_map<vex::Entity, std::vector<GameObject*>> hierarchyMap;
370 std::vector<GameObject*> rootObjects;
372 for (
const auto& objPtr : m_objects) {
373 if (!objPtr || !objPtr->isValid())
continue;
383 hierarchyMap[parentEntity].push_back(obj);
385 rootObjects.push_back(obj);
389 std::vector<GameObject*> sortedObjects;
390 std::deque<GameObject*> processQueue;
392 for (
auto* root : rootObjects) {
393 processQueue.push_back(root);
396 while (!processQueue.empty()) {
397 GameObject* currentObj = processQueue.front();
398 processQueue.pop_front();
400 sortedObjects.push_back(currentObj);
404 if (hierarchyMap.find(currentEntity) != hierarchyMap.end()) {
405 for (
auto* child : hierarchyMap[currentEntity]) {
406 processQueue.push_back(child);
411 nlohmann::json objectsArray = nlohmann::json::array();
414 nlohmann::json objJson;
417 objJson[
"name"] = name;
418 objJson[
"type"] = obj->getObjectType();
430 nlohmann::json componentsArray = nlohmann::json::array();
431 const auto& regNames = ComponentRegistry::getInstance().getRegisteredNames();
433 for (
const auto& compName : regNames) {
434 nlohmann::json compData = ComponentRegistry::getInstance().saveComponent(*obj, compName);
435 if (!compData.is_null()) {
436 compData[
"type"] = compName;
437 componentsArray.push_back(compData);
441 objJson[
"components"] = componentsArray;
442 objectsArray.push_back(objJson);
445 sceneJson[
"objects"] = objectsArray;
447 std::ofstream file(path);
448 if (file.is_open()) {
449 file << std::setw(4) << sceneJson << std::endl;
450 log(
"Scene saved successfully to: %s", path.c_str());
452 log(
"Error: Failed to write scene file to: %s", path.c_str());
457 if (!gameObject)
return;
459 auto it = std::find_if(m_addedObjects.begin(), m_addedObjects.end(),
460 [gameObject](
const std::shared_ptr<GameObject>& ptr) {
461 return ptr.get() == gameObject;
464 if (it != m_addedObjects.end()) {
465 m_objects.push_back(*it);
466 m_addedObjects.erase(it);
470 m_objects.emplace_back(std::shared_ptr<GameObject>(gameObject));
Contains basic components like transform, camera, name components..
This file defines in engine CameraObject class.
Contains ComponentRegistry class used to have register of posible to create components....
This file defines GameObjectFactory class used to auto register GameObjects.
This file defines functions for creating model objects and mesh components.
This file defines SceneManager class.
This file defines VirtualFileSystem and VPKStream classes.
Class for interaction with engine systems.
GameObject * create(const std::string &type, Engine &engine, const std::string &name)
Create a GameObject instance of the specified type string.
static GameObjectFactory & getInstance()
Get the singleton instance of the GameObjectFactory.
Its base class for all game objects, eg. Player, Enemy, Weapon. Your Class needs to inherit from it.
bool HasComponent() const
Function that checks if the GameObject has a component.
vex::Entity GetEntity() const
Function that returns entity object, which is the entity associated with this GameObject....
void ParentTo(vex::Entity entity)
Function that parent this Object to another GameObject. You need to pass another GameObject's entity.
T & GetComponent()
Function that returns reference to a requested component.
Scene(const std::string &path, Engine &engine)
Constructor creates empty scene object.
~Scene()
Destructor clears the current scene.
GameObject * GetGameObjectByEntity(vex::Entity &entity)
Function to get a game object by its entity ID.
void AddEditorGameObject(GameObject *gameObject)
Promotes a temporary GameObject (e.g., created by the Editor) to a persistent Scene object.
void RegisterGameObject(GameObject *gameObject)
Registers a game object into the scene's internal storage.
std::vector< GameObject * > GetAllGameObjectsByClassName(const std::string &classname)
Function to get all game objects of a specific class type.
void FlushDestructionQueue()
Processes the queue of objects marked for destruction.
void Save(const std::string &outputPath)
Saves the current state of the scene to a JSON file.
void DestroyGameObject(GameObject *gameObject)
Marks a game object for destruction.
void AddGameObject(std::unique_ptr< GameObject > gameObject)
[Deprecated] Function to add a game object to the scene.
std::vector< GameObject * > GetAllGameObjectsByName(const std::string &name)
Function to get all game objects with a specific name.
void load()
Loads the scene data from the file path specified in the constructor.
void sceneBegin()
Calls BeginPlay on all objects in the scene.
void sceneUpdate(float deltaTime)
Updates all objects in the scene.
uint32_t Entity
Type alias representing a unique entity identifier in the ECS system.
void VEX_EXPORT throw_error(const std::string &msg)
Throws an error or terminates the application.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
void VEX_EXPORT handle_critical_exception(const std::exception &e)
Handles a critical exception, forcing termination.
std::string VEX_EXPORT GetAssetPath(const std::string &relativePath)
Resolves the absolute path of an asset based on the current build configuration.
void VEX_EXPORT handle_exception(const std::exception &e)
Handles an exception based on build configuration.
constexpr Entity NULL_ENTITY
Special entity value indicating an invalid or null entity.
Struct that simply contains name of the entity. It is used to identify entity and needs to be unique.
This struct is used to hold all environment settings. eg. shading details or lighting setup.
bool screenDither
Enables screen dithering. (PS1 style dithering).
float ambientLightStrength
Ambient light strength.
bool textureQuantization
Enables texture quantization. (PS1 style texture quantization).
glm::vec3 sunDirection
Sun direction.
bool screenQuantization
Enables whole screen color quantization. (PS1 style color compresion artifacts).
bool gourardShading
Enables Gouraud shading.
bool ntfsArtifacts
Enables CRT TVs artifacts.
glm::vec3 clearColor
Background color (Esentially sky color if not implemented any skybox).
bool affineWarping
Enables affine texxture warping. (PS1 style texture warping in extreme camera angles).
bool passiveVertexJitter
Enables passive vertex jittering. (PS1 style jitter when camera is not moving).
glm::vec3 sunLight
Sun light color.
bool vertexSnapping
Enables vertex snapping. (PS1 style jitter when camera is moving).
glm::vec3 ambientLight
Ambient light color.