2#include <QJsonDocument>
10#include <unordered_map>
13 inline double numberOr(
const QJsonObject& obj,
const char* key,
double fallback) {
14 const QJsonValue value = obj.value(key);
15 return value.isDouble() ? value.toDouble(fallback) : fallback;
26 inline glm::vec3
jsonToVec3(QJsonArray arr,
const glm::vec3& fallback = glm::vec3(0.0f)) {
27 if (arr.size() != 3) {
30 if (!arr[0].isDouble() || !arr[1].isDouble() || !arr[2].isDouble()) {
33 return {arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble()};
38 obj[
"tempK"] = props.
tempK;
98 root[
"engineVersion"] =
"1.0.0";
101 QJsonObject settings;
103 settings[
"gravitationalConstant"] = sceneManager->
physicsSystem->getGravitationalConstant();
104 settings[
"simSpeed"] = sceneManager->
getSimSpeed();
105 settings[
"ambientTemperature"] = sceneManager->
physicsSystem->getAmbientTemperature();
106 root[
"settings"] = settings;
110 QJsonObject cameraJson;
112 cameraJson[
"yaw"] = camera->
yaw;
113 cameraJson[
"pitch"] = camera->
pitch;
115 cameraJson[
"followTargetId"] =
static_cast<double>(target->getObjectID());
116 cameraJson[
"followTargetName"] = QString::fromStdString(target->getName());
118 root[
"camera"] = cameraJson;
121 QJsonObject selectionJson;
122 for (uint32_t selectedID : sceneManager->
selectedIDs) {
124 selectionJson[
"objectId"] =
static_cast<double>(selected->getObjectID());
125 selectionJson[
"objectName"] = QString::fromStdString(selected->getName());
129 if (!selectionJson.isEmpty()) {
130 root[
"selection"] = selectionJson;
133 QJsonArray objectsArray;
134 for (
const auto& objPtr : sceneManager->
getObjects()) {
137 objJson[
"id"] =
static_cast<double>(obj->
getObjectID());
138 objJson[
"meshName"] = QString::fromStdString(obj->
getMeshName());
139 objJson[
"name"] = QString::fromStdString(obj->
getName());
142 QJsonObject optionsJson;
143 std::visit([&](
auto&& opt){
144 using T = std::decay_t<
decltype(opt)>;
151 if constexpr (std::is_same_v<T, PointMassOptions>) {
152 optionsJson[
"type"] =
"PointMassOptions";
160 else if constexpr (std::is_same_v<T, RigidBodyOptions>) {
161 optionsJson[
"type"] =
"RigidBodyOptions";
170 optionsJson[
"type"] =
"ObjectOptions";
173 optionsJson[
"data"] = data;
176 objJson[
"options"] = optionsJson;
177 objectsArray.append(objJson);
179 root[
"objects"] = objectsArray;
181 QJsonDocument doc(root);
182 QFile file(filename);
183 if (!file.open(QIODevice::WriteOnly)) {
184 qWarning() <<
"Failed to open file for saving:" << filename;
188 file.write(doc.toJson(QJsonDocument::Indented));
194 QFile file(filename);
195 if (!file.open(QIODevice::ReadOnly)) {
196 qWarning() <<
"Failed to open file for loading:" << filename;
200 QByteArray data = file.readAll();
201 QJsonDocument doc = QJsonDocument::fromJson(data);
202 if (doc.isNull() || !doc.isObject()) {
203 qWarning() <<
"Invalid JSON format in file:" << filename;
207 QJsonObject root = doc.object();
212 if (root.contains(
"settings") && root[
"settings"].isObject()) {
213 QJsonObject settings = root[
"settings"].toObject();
214 if (settings[
"gravity"].isArray()) {
224 std::unordered_map<uint32_t, SceneObject*> objectsBySavedId;
225 std::unordered_map<std::string, SceneObject*> objectsByName;
227 if (root.contains(
"objects") && root[
"objects"].isArray()) {
228 QJsonArray objectsArray = root[
"objects"].toArray();
229 for (
const auto &value : objectsArray) {
230 if (!value.isObject())
continue;
231 QJsonObject objJson = value.toObject();
233 uint32_t
id =
static_cast<uint32_t
>(objJson[
"id"].toDouble());
234 std::string meshName = objJson[
"meshName"].toString().toStdString();
235 std::string objName = objJson[
"name"].toString().toStdString();
236 std::string shaderName = objJson[
"shader"].toString().toStdString();
239 if (objJson.contains(
"options") && objJson[
"options"].isObject()) {
240 QJsonObject optionsJson = objJson[
"options"].toObject();
241 QString type = optionsJson[
"type"].toString();
242 QJsonObject data = optionsJson[
"data"].toObject();
249 if (type ==
"PointMassOptions") {
251 pointOpt.
base = base;
252 pointOpt.
isStatic = data[
"isStatic"].toBool();
253 pointOpt.
mass = data[
"mass"].toDouble();
257 else if (type ==
"RigidBodyOptions") {
261 data[
"isStatic"].toBool(),
262 data[
"mass"].toDouble(),
276 objectsBySavedId[id] = createObj;
277 objectsByName[objName] = createObj;
278 if (createObj->
getPhysicsBody() && objJson[
"options"].isObject()) {
279 QJsonObject optionsJson = objJson[
"options"].toObject();
280 QJsonObject data = optionsJson[
"data"].toObject();
281 if (data[
"thermal"].isObject()) {
289 auto resolveObject = [&](
const QJsonObject& obj,
const char* idKey,
const char* nameKey) ->
SceneObject* {
290 const QJsonValue idValue = obj.value(idKey);
291 if (idValue.isDouble()) {
292 const auto it = objectsBySavedId.find(
static_cast<uint32_t
>(idValue.toDouble()));
293 if (it != objectsBySavedId.end()) {
298 const std::string name = obj.value(nameKey).toString().toStdString();
299 const auto nameIt = objectsByName.find(name);
300 return nameIt != objectsByName.end() ? nameIt->second :
nullptr;
303 bool restoredCameraTarget =
false;
304 if (root[
"camera"].isObject() && sceneManager->
scene && sceneManager->
scene->
getCamera()) {
305 QJsonObject cameraJson = root[
"camera"].toObject();
307 const glm::vec3 position =
JsonUtils::jsonToVec3(cameraJson[
"position"].toArray(), glm::vec3(0.0f, 10.0f, 30.0f));
310 camera->
setView(position, yaw, pitch);
312 if (
SceneObject* target = resolveObject(cameraJson,
"followTargetId",
"followTargetName")) {
314 restoredCameraTarget =
true;
318 if (root[
"selection"].isObject()) {
319 if (
SceneObject* selected = resolveObject(root[
"selection"].toObject(),
"objectId",
"objectName")) {
322 }
else if (!restoredCameraTarget) {
std::variant< ObjectOptions, PointMassOptions, RigidBodyOptions > CreationOptions
float thermalMassFraction
float linearExpansionCoeff
float latentHeatVaporization
float vaporizationProgress
float specificHeatTempCoeff
float absorptivityTempCoeff
float conductivityTempCoeff
float emissivityTempCoeff
void setView(const glm::vec3 &newPosition, double newYaw, double newPitch)
virtual double getMass(BodyLock lock) const
glm::vec3 getVelocity(BodyLock lock) const
virtual ThermalProperties getThermalProperties(BodyLock lock) const
bool getIsStatic(BodyLock lock) const
virtual void setThermalProperty(const ThermalProperties &newProps, BodyLock lock)
static Shader * getShader(const std::string &name)
static std::string getShaderName(const Shader *shader)
SceneObject * createObject(const std::string &meshName, Shader *shader=ResourceManager::getShader("basic"), const CreationOptions &=ObjectOptions{})
const SceneObject * getCameraTarget() const
SceneObject * getObjectByID(uint32_t objectID) const
void selectObject(SceneObject *obj)
void setSimSpeed(float newSpeed)
std::unordered_set< uint32_t > selectedIDs
void setGlobalAcceleration(const glm::vec3 &newAcceleration) const
std::unique_ptr< Physics::PhysicsSystem > physicsSystem
void setObjectName(SceneObject *obj, const std::string &newName)
void setCameraTarget(SceneObject *target)
glm::vec3 getGlobalAcceleration() const
float getSimSpeed() const
void setSelectFor(SceneObject *obj, bool flag=true)
const std::vector< std::unique_ptr< SceneObject > > & getObjects() const
CreationOptions getCreationOptions() const
glm::vec3 getRotation() const
glm::vec3 getPosition() const
glm::vec3 getScale() const
const std::string & getMeshName() const
Shader * getShader() const override
Gets the shader used to render this object.
Physics::PhysicsBody * getPhysicsBody() const
const std::string & getName() const
uint32_t getObjectID() const override
Gets the unique identifier for this object.
SceneSerializer(SceneManager *sceneMgr)
bool loadFromJson(const QString &filename)
bool saveToJson(const QString &filename) const
ThermalProperties jsonToThermal(const QJsonObject &obj, const ThermalProperties &fallback)
QJsonObject thermalToJson(const ThermalProperties &props)
QJsonArray vec3ToJson(const glm::vec3 &v)
glm::vec3 jsonToVec3(QJsonArray arr, const glm::vec3 &fallback=glm::vec3(0.0f))
double numberOr(const QJsonObject &obj, const char *key, double fallback)
static RigidBodyOptions Box(ObjectOptions base, bool isStatic=false, double mass=1.0, glm::vec3 velocity=glm::vec3(0.0f))