ObjectParser.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. #include "ObjectParser.h"
  2. #include "EffectParser.h"
  3. #include "MaterialParser.h"
  4. #include "ModelParser.h"
  5. #include "ShaderConstantParser.h"
  6. #include "TextureParser.h"
  7. #include "UserSettingParser.h"
  8. #include "WallpaperEngine/Data/Builders/ColorBuilder.h"
  9. #include "WallpaperEngine/Data/Model/Object.h"
  10. #include "WallpaperEngine/Data/Model/Project.h"
  11. #include "WallpaperEngine/Logging/Log.h"
  12. #include <glm/gtc/constants.hpp>
  13. #include <sstream>
  14. using namespace WallpaperEngine::Data::Parsers;
  15. using namespace WallpaperEngine::Data::Model;
  16. ObjectUniquePtr ObjectParser::parse (const JSON& it, const Project& project) {
  17. const auto imageIt = it.find ("image");
  18. const auto soundIt = it.find ("sound");
  19. const auto particleIt = it.find ("particle");
  20. const auto textIt = it.find ("text");
  21. const auto lightIt = it.find ("light");
  22. // "shape" refers to VolumeLight
  23. const auto shapeIt = it.find ("shape");
  24. // some particle objects have numeric 'name' fields, so handle type mismatches gracefully
  25. ObjectData basedata;
  26. try {
  27. basedata = ObjectData {
  28. .id = it.require<int> ("id", "Object must have an id"),
  29. .name = it.require<std::string> ("name", "Object must have a name"),
  30. .dependencies = parseDependencies (it),
  31. .parent = it.optional<int> ("parent"),
  32. .attachment = it.optional<std::string> ("attachment"),
  33. .sortOrder = it.optional<int> ("sortorder"),
  34. .origin = it.user ("origin", project.properties, glm::vec3 (0.0f)),
  35. .groupScale = it.user ("scale", project.properties, glm::vec3 (1.0f)),
  36. .groupAngles = it.user ("angles", project.properties, glm::vec3 (0.0f)),
  37. .groupVisible = it.user ("visible", project.properties, true),
  38. .groupParallaxDepth = it.user ("parallaxDepth", project.properties, glm::vec2 (1.0f)),
  39. };
  40. } catch (const std::exception& e) {
  41. sLog.error ("Error parsing object base data: ", e.what ());
  42. const auto idIt = it.find ("id");
  43. const auto nameIt = it.find ("name");
  44. int id = (idIt != it.end () && idIt->is_number ()) ? idIt->get<int> () : -1;
  45. std::string name = "unknown";
  46. if (nameIt != it.end ()) {
  47. if (nameIt->is_string ()) {
  48. name = nameIt->get<std::string> ();
  49. } else if (nameIt->is_number ()) {
  50. name = std::to_string (nameIt->get<int> ());
  51. }
  52. }
  53. basedata = ObjectData {
  54. .id = id,
  55. .name = name,
  56. .dependencies = parseDependencies (it),
  57. .parent = it.optional<int> ("parent"),
  58. .attachment = it.optional<std::string> ("attachment"),
  59. .sortOrder = it.optional<int> ("sortorder"),
  60. .origin = it.user ("origin", project.properties, glm::vec3 (0.0f)),
  61. .groupScale = it.user ("scale", project.properties, glm::vec3 (1.0f)),
  62. .groupAngles = it.user ("angles", project.properties, glm::vec3 (0.0f)),
  63. .groupVisible = it.user ("visible", project.properties, true),
  64. .groupParallaxDepth = it.user ("parallaxDepth", project.properties, glm::vec2 (1.0f)),
  65. };
  66. }
  67. if (imageIt != it.end () && imageIt->is_string ()) {
  68. return parseImage (it, project, std::move (basedata), *imageIt);
  69. } else if (soundIt != it.end () && soundIt->is_array ()) {
  70. return parseSound (it, project, std::move (basedata));
  71. } else if (particleIt != it.end () && !particleIt->is_null ()) {
  72. return parseParticle (it, project, std::move (basedata));
  73. } else if (textIt != it.end () && !textIt->is_null ()) {
  74. return parseText (it, project, std::move (basedata));
  75. } else if (lightIt != it.end () && !lightIt->is_null ()) {
  76. sLog.error ("Light objects are not supported yet");
  77. } else if (shapeIt != it.end () && !shapeIt->is_null ()) {
  78. sLog.error ("VolumeLight objects are not supported yet");
  79. } else {
  80. if (!it.optional ("solid", false)) {
  81. // TODO: re-evaluate - some objects contain other objects and aren't really anything special
  82. sLog.error ("Unknown object type found: ", it.dump ());
  83. }
  84. }
  85. return std::make_unique<Object> (std::move (basedata));
  86. }
  87. std::vector<int> ObjectParser::parseDependencies (const JSON& it) {
  88. const auto dependenciesIt = it.find ("dependencies");
  89. if (dependenciesIt == it.end () || !dependenciesIt->is_array ()) {
  90. return {};
  91. }
  92. std::vector<int> result = {};
  93. for (const auto& cur : *dependenciesIt) {
  94. result.push_back (cur);
  95. }
  96. return result;
  97. }
  98. SoundUniquePtr ObjectParser::parseSound (const JSON& it, const Project& project, ObjectData base) {
  99. const auto soundIt = it.require ("sound", "Object must have a sound");
  100. std::vector<std::string> sounds = {};
  101. for (const auto& cur : soundIt) {
  102. sounds.push_back (cur);
  103. }
  104. return std::make_unique<Sound> (
  105. std::move (base),
  106. SoundData {
  107. .playbackmode = parsePlaybackMode (it.optional ("playbackmode", std::string ("single"))),
  108. .sounds = sounds,
  109. .volume = it.user<float> ("volume", project.properties, 1.0f),
  110. .startsilent = it.optional<bool> ("startsilent"),
  111. }
  112. );
  113. }
  114. SoundPlaybackMode ObjectParser::parsePlaybackMode (const std::string& mode) {
  115. if (mode == "loop") {
  116. return PlaybackMode_Loop;
  117. }
  118. if (mode == "random") {
  119. return PlaybackMode_Random;
  120. }
  121. return PlaybackMode_Single;
  122. }
  123. uint32_t ObjectParser::parseAlignment (const std::string& alignment) {
  124. uint32_t result = ImageAlignment_Center;
  125. if (alignment.find ("top") != std::string::npos) {
  126. result |= ImageAlignment_Top;
  127. } else if (alignment.find ("bottom") != std::string::npos) {
  128. result |= ImageAlignment_Bottom;
  129. }
  130. if (alignment.find ("left") != std::string::npos) {
  131. result |= ImageAlignment_Left;
  132. } else if (alignment.find ("right") != std::string::npos) {
  133. result |= ImageAlignment_Right;
  134. }
  135. return result;
  136. }
  137. TextUniquePtr ObjectParser::parseText (const JSON& it, const Project& project, ObjectData base) {
  138. const auto& effects = it.optional ("effects");
  139. return std::make_unique<Text> (
  140. std::move (base),
  141. TextData {
  142. .text = it.user ("text", project.properties),
  143. .font = it.optional ("font", std::string ()),
  144. .pointSize = it.user ("pointsize", project.properties, 32.0f),
  145. .size = it.optional ("size", glm::vec2 (0.0f)),
  146. .scale = it.user ("scale", project.properties, glm::vec3 (1.0f)),
  147. .color = it.color ("color", project.properties, Builders::ColorBuilder::White),
  148. .alpha = it.user ("alpha", project.properties, 1.0f),
  149. .visible = it.user ("visible", project.properties, true),
  150. .parallaxDepth = it.user ("parallaxDepth", project.properties, glm::vec2 (1.0f)),
  151. .alignment = it.optional ("horizontalalign", it.optional ("alignment", std::string ("center"))),
  152. .verticalalign = it.optional ("verticalalign", std::string ("center")),
  153. .padding = it.optional ("padding", glm::vec2 (0.0f)),
  154. .effects = effects.has_value () ? parseEffects (*effects, project) : std::vector<ImageEffectUniquePtr> {},
  155. .limitWidth = it.user ("limitwidth", project.properties, false),
  156. .maxWidth = it.user ("maxwidth", project.properties, 500.0f),
  157. .limitRows = it.user ("limitrows", project.properties, false),
  158. .maxRows = it.user ("maxrows", project.properties, 1),
  159. .limitUseEllipsis = it.user ("limituseellipsis", project.properties, false),
  160. }
  161. );
  162. }
  163. ImageUniquePtr
  164. ObjectParser::parseImage (const JSON& it, const Project& project, ObjectData base, const std::string& image) {
  165. const auto& properties = project.properties;
  166. const auto& effects = it.optional ("effects");
  167. const auto& animationLayers = it.optional ("animationlayers");
  168. auto result = std::make_unique<Image> (
  169. std::move (base),
  170. ImageData {
  171. .scale = it.user ("scale", properties, glm::vec3 (1.0f)),
  172. .angles = it.user ("angles", properties, glm::vec3 (0.0f)),
  173. .visible = it.user ("visible", properties, true),
  174. .alpha = it.user ("alpha", properties, 1.0f),
  175. .color = it.color ("color", properties, Builders::ColorBuilder::White),
  176. .alignment = parseAlignment (
  177. it.optional ("horizontalalign", it.optional ("alignment", std::string ("center")))
  178. ),
  179. .size = it.user ("size", properties, glm::vec2 (0.0f))->value->getVec2 (),
  180. .parallaxDepth = it.user ("parallaxDepth", properties, glm::vec2 (1.0f)),
  181. .colorBlendMode = it.user ("colorBlendMode", properties, 0),
  182. .brightness = it.user ("brightness", properties, 1.0f),
  183. .clampUVs = it.optional ("clampuvs", false),
  184. .model = ModelParser::load (project, image),
  185. .effects = effects.has_value () ? parseEffects (*effects, project) : std::vector<ImageEffectUniquePtr> {},
  186. .animationLayers = animationLayers.has_value () ? parseAnimationLayers (*animationLayers, project)
  187. : std::vector<ImageAnimationLayerUniquePtr> {},
  188. }
  189. );
  190. const auto instance = it.optional ("instance");
  191. if (instance.has_value () && instance->is_object () && !result->model->material->passes.empty ()) {
  192. auto& firstPass = **result->model->material->passes.begin ();
  193. const auto instanceTextures = instance->optional ("textures");
  194. if (instanceTextures.has_value ()) {
  195. // the instance's textures replace the material's, a solid layer's util/white included
  196. for (const auto& [index, texture] : TextureParser::parseTextureMap (*instanceTextures)) {
  197. firstPass.textures.insert_or_assign (index, texture);
  198. }
  199. }
  200. const auto instanceUserTextures = instance->optional ("usertextures");
  201. if (instanceUserTextures.has_value ()) {
  202. for (const auto& [index, texture] : TextureParser::parseTextureMap (*instanceUserTextures)) {
  203. firstPass.usertextures.insert_or_assign (index, texture);
  204. }
  205. }
  206. }
  207. return result;
  208. }
  209. std::vector<ImageEffectUniquePtr> ObjectParser::parseEffects (const JSON& it, const Project& project) {
  210. if (!it.is_array ()) {
  211. return {};
  212. }
  213. std::vector<ImageEffectUniquePtr> result = {};
  214. for (const auto& cur : it) {
  215. result.push_back (parseEffect (cur, project));
  216. }
  217. return result;
  218. }
  219. ImageEffectUniquePtr ObjectParser::parseEffect (const JSON& it, const Project& project) {
  220. const auto& passsOverrides = it.optional ("passes");
  221. return std::make_unique<ImageEffect> (ImageEffect {
  222. .id = it.optional<int> ("id", -1),
  223. .name = it.optional<std::string> ("name", "Effect without name"),
  224. .visible = it.user ("visible", project.properties, true),
  225. .passOverrides = passsOverrides.has_value () ? parseEffectPassOverrides (passsOverrides.value (), project)
  226. : std::vector<ImageEffectPassOverrideUniquePtr> {},
  227. .effect = EffectParser::load (project, it.require ("file", "Image effect must have an effect")) });
  228. }
  229. std::vector<ImageEffectPassOverrideUniquePtr>
  230. ObjectParser::parseEffectPassOverrides (const JSON& it, const Project& project) {
  231. if (!it.is_array ()) {
  232. return {};
  233. }
  234. std::vector<ImageEffectPassOverrideUniquePtr> result = {};
  235. for (const auto& cur : it) {
  236. result.push_back (parseEffectPass (cur, project));
  237. }
  238. return result;
  239. }
  240. ImageEffectPassOverrideUniquePtr ObjectParser::parseEffectPass (const JSON& it, const Project& project) {
  241. const auto& combos = it.optional ("combos");
  242. const auto& textures = it.optional ("textures");
  243. const auto& constants = it.optional ("constantshadervalues");
  244. const auto& usertextures = it.optional ("usertextures");
  245. return std::make_unique<ImageEffectPassOverride> (ImageEffectPassOverride {
  246. .id = it.optional<int> ("id", -1),
  247. .combos = combos.has_value () ? parseComboMap (combos.value ()) : ComboMap {},
  248. .constants
  249. = constants.has_value () ? ShaderConstantParser::parse (constants.value (), project) : ShaderConstantMap {},
  250. .textures = textures.has_value () ? TextureParser::parseTextureMap (textures.value ()) : TextureMap {},
  251. .usertextures
  252. = usertextures.has_value () ? TextureParser::parseTextureMap (usertextures.value ()) : TextureMap {},
  253. });
  254. }
  255. ComboMap ObjectParser::parseComboMap (const JSON& it) {
  256. if (!it.is_object ()) {
  257. return {};
  258. }
  259. ComboMap result = {};
  260. for (const auto& cur : it.items ()) {
  261. result.emplace (cur.key (), cur.value ());
  262. }
  263. return result;
  264. }
  265. std::vector<ImageAnimationLayerUniquePtr> ObjectParser::parseAnimationLayers (const JSON& it, const Project& project) {
  266. if (!it.is_array ()) {
  267. return {};
  268. }
  269. std::vector<ImageAnimationLayerUniquePtr> result = {};
  270. for (const auto& cur : it.items ()) {
  271. result.push_back (parseAnimationLayer (cur.value (), project));
  272. }
  273. return result;
  274. }
  275. ImageAnimationLayerUniquePtr ObjectParser::parseAnimationLayer (const JSON& it, const Project& project) {
  276. const auto& properties = project.properties;
  277. return std::make_unique<ImageAnimationLayer> (ImageAnimationLayer {
  278. .id = it.require<int> ("id", "Animation layer must have an id"),
  279. .name = it.optional<std::string> ("name", ""),
  280. .rate = it.user ("rate", properties, 1.0f),
  281. .visible = it.user ("visible", properties, false),
  282. .blend = it.user ("blend", properties, 1.0f),
  283. .animation = it.user ("animation", properties, 0),
  284. });
  285. }
  286. ParticleUniquePtr ObjectParser::parseParticle (const JSON& it, const Project& project, ObjectData base) {
  287. try {
  288. const auto& properties = project.properties;
  289. const auto particleIt = it.find ("particle");
  290. if (particleIt == it.end ()) {
  291. sLog.error ("Particle object must have a particle definition");
  292. return std::make_unique<Particle> (
  293. std::move (base),
  294. ParticleData {
  295. .scale = it.user ("scale", properties, glm::vec3 (1.0f)),
  296. .angles = it.user ("angles", properties, glm::vec3 (0.0f)),
  297. .visible = it.user ("visible", properties, true),
  298. .parallaxDepth = it.user ("parallaxDepth", properties, glm::vec2 (1.0f)),
  299. .particleFile = "",
  300. .animationMode = "sequence",
  301. .sequenceMultiplier = 1.0f,
  302. .maxCount = 100,
  303. .startTime = 0.0f,
  304. .flags = 0,
  305. .material = nullptr,
  306. .emitters = {},
  307. .initializers = {},
  308. .operators = {},
  309. .renderers = {},
  310. .controlPoints = {},
  311. .children = {},
  312. .instanceOverride = {
  313. .enabled = Builders::UserSettingBuilder::fromValue(false),
  314. .alpha = Builders::UserSettingBuilder::fromValue(1.0f),
  315. .size = Builders::UserSettingBuilder::fromValue(1.0f),
  316. .lifetime = Builders::UserSettingBuilder::fromValue(1.0f),
  317. .rate = Builders::UserSettingBuilder::fromValue(1.0f),
  318. .speed = Builders::UserSettingBuilder::fromValue(1.0f),
  319. .count = Builders::UserSettingBuilder::fromValue(1.0f),
  320. .color = Builders::UserSettingBuilder::fromValue(1.0f),
  321. .colorn = Builders::UserSettingBuilder::fromValue(1.0f),
  322. },
  323. }
  324. );
  325. }
  326. std::string particleFile;
  327. if (particleIt->is_string ()) {
  328. particleFile = particleIt->get<std::string> ();
  329. }
  330. JSON particleJson = JSON::object ();
  331. if (!particleFile.empty ()) {
  332. try {
  333. particleJson
  334. = WallpaperEngine::Data::JSON::JSON::parseAsset (project.assetLocator->readString (particleFile));
  335. } catch (std::runtime_error& e) {
  336. sLog.error ("Cannot load particle file: ", particleFile, " - ", e.what ());
  337. }
  338. } else if (particleIt->is_object ()) {
  339. particleJson = *particleIt;
  340. }
  341. // field is named "emitter" not "emitters"
  342. std::vector<ParticleEmitter> emitters;
  343. const auto emittersIt = particleJson.find ("emitter");
  344. if (emittersIt != particleJson.end () && emittersIt->is_array ()) {
  345. for (const auto& emitter : *emittersIt) {
  346. emitters.push_back (parseParticleEmitter (emitter));
  347. }
  348. }
  349. // field is named "initializer" not "initializers"
  350. std::vector<ParticleInitializerUniquePtr> initializers;
  351. const auto initializersIt = particleJson.find ("initializer");
  352. if (initializersIt != particleJson.end () && initializersIt->is_array ()) {
  353. for (const auto& initializer : *initializersIt) {
  354. auto init = parseParticleInitializer (initializer, project.properties);
  355. if (init) {
  356. initializers.push_back (std::move (init));
  357. }
  358. }
  359. }
  360. // field is named "operator" not "operators"
  361. std::vector<ParticleOperatorUniquePtr> operators;
  362. const auto operatorsIt = particleJson.find ("operator");
  363. if (operatorsIt != particleJson.end () && operatorsIt->is_array ()) {
  364. for (const auto& op : *operatorsIt) {
  365. auto oper = parseParticleOperator (op, project.properties);
  366. if (oper) {
  367. operators.push_back (std::move (oper));
  368. }
  369. }
  370. }
  371. // field is named "renderer" not "renderers"
  372. std::vector<ParticleRenderer> renderers;
  373. const auto renderersIt = particleJson.find ("renderer");
  374. if (renderersIt != particleJson.end () && renderersIt->is_array ()) {
  375. for (const auto& renderer : *renderersIt) {
  376. renderers.push_back (parseParticleRenderer (renderer));
  377. }
  378. }
  379. if (renderers.empty ()) {
  380. renderers.push_back (
  381. ParticleRenderer {
  382. .name = "sprite",
  383. .length = 0.05f,
  384. .maxLength = 10.0f,
  385. .minLength = 0.0f,
  386. .subdivision = 1.0f,
  387. .segments = 4.0f,
  388. .uvScale = 1.0f,
  389. .uvScrolling = false,
  390. .uvSmoothing = true,
  391. .fadeAlpha = false,
  392. .fadeSize = false,
  393. }
  394. );
  395. }
  396. // field is named "controlpoint" not "controlpoints"
  397. std::vector<ParticleControlPoint> controlPoints;
  398. const auto controlPointsIt = particleJson.find ("controlpoint");
  399. if (controlPointsIt != particleJson.end () && controlPointsIt->is_array ()) {
  400. for (const auto& cp : *controlPointsIt) {
  401. controlPoints.push_back (parseParticleControlPoint (cp));
  402. }
  403. }
  404. std::vector<ParticleChild> children;
  405. const auto childrenIt = particleJson.optional ("children");
  406. if (childrenIt.has_value () && childrenIt->is_array ()) {
  407. for (const auto& child : *childrenIt) {
  408. children.push_back (parseParticleChild (child, project));
  409. }
  410. }
  411. ParticleInstanceOverride instanceOverride = {
  412. .enabled = Builders::UserSettingBuilder::fromValue (false),
  413. .alpha = Builders::UserSettingBuilder::fromValue (1.0f),
  414. .size = Builders::UserSettingBuilder::fromValue (1.0f),
  415. .lifetime = Builders::UserSettingBuilder::fromValue (1.0f),
  416. .rate = Builders::UserSettingBuilder::fromValue (1.0f),
  417. .speed = Builders::UserSettingBuilder::fromValue (1.0f),
  418. .count = Builders::UserSettingBuilder::fromValue (1.0f),
  419. .color = Builders::UserSettingBuilder::fromValue (1.0f),
  420. .colorn = Builders::UserSettingBuilder::fromValue (1.0f),
  421. };
  422. const auto instanceOverrideIt = it.optional ("instanceoverride");
  423. if (instanceOverrideIt.has_value ()) {
  424. instanceOverride = parseParticleInstanceOverride (*instanceOverrideIt, project.properties);
  425. }
  426. // particles reference material definitions directly, not model files, so wrap it in a model structure
  427. ModelUniquePtr material = nullptr;
  428. const auto materialIt = particleJson.find ("material");
  429. if (materialIt != particleJson.end () && materialIt->is_string ()) {
  430. try {
  431. std::string materialPath = materialIt->get<std::string> ();
  432. auto mat = MaterialParser::load (project, materialPath);
  433. material = std::make_unique<ModelStruct> (ModelStruct {
  434. .filename = materialPath,
  435. .material = std::move (mat),
  436. .solidlayer = false,
  437. .fullscreen = false,
  438. .passthrough = false,
  439. .autosize = false,
  440. .projectlayer = false,
  441. .nopadding = false,
  442. .width = std::nullopt,
  443. .height = std::nullopt,
  444. .puppet = std::nullopt,
  445. });
  446. } catch (std::runtime_error& e) {
  447. sLog.error ("Cannot load particle material: ", materialIt->get<std::string> (), " - ", e.what ());
  448. }
  449. }
  450. std::string animationMode = "sequence";
  451. const auto animModeIt = particleJson.find ("animationmode");
  452. if (animModeIt != particleJson.end () && animModeIt->is_string ()) {
  453. animationMode = animModeIt->get<std::string> ();
  454. }
  455. float sequenceMultiplier = 1.0f;
  456. uint32_t maxCount = 100;
  457. float startTime = 0.0f;
  458. uint32_t flags = 0;
  459. const auto seqMultIt = particleJson.find ("sequencemultiplier");
  460. if (seqMultIt != particleJson.end () && seqMultIt->is_number ()) {
  461. sequenceMultiplier = seqMultIt->get<float> ();
  462. }
  463. const auto maxCountIt = particleJson.find ("maxcount");
  464. if (maxCountIt != particleJson.end () && maxCountIt->is_number ()) {
  465. maxCount = maxCountIt->get<uint32_t> ();
  466. }
  467. const auto startTimeIt = particleJson.find ("starttime");
  468. if (startTimeIt != particleJson.end () && startTimeIt->is_number ()) {
  469. startTime = startTimeIt->get<float> ();
  470. }
  471. const auto flagsIt = particleJson.find ("flags");
  472. if (flagsIt != particleJson.end () && flagsIt->is_number ()) {
  473. flags = flagsIt->get<uint32_t> ();
  474. }
  475. return std::make_unique<Particle> (
  476. std::move (base),
  477. ParticleData {
  478. .scale = it.user ("scale", properties, glm::vec3 (1.0f)),
  479. .angles = it.user ("angles", properties, glm::vec3 (0.0f)),
  480. .visible = it.user ("visible", properties, true),
  481. .parallaxDepth = it.user ("parallaxDepth", properties, glm::vec2 (1.0f)),
  482. .particleFile = particleFile,
  483. .animationMode = animationMode,
  484. .sequenceMultiplier = sequenceMultiplier,
  485. .maxCount = maxCount,
  486. .startTime = startTime,
  487. .flags = flags,
  488. .material = std::move (material),
  489. .emitters = std::move (emitters),
  490. .initializers = std::move (initializers),
  491. .operators = std::move (operators),
  492. .renderers = std::move (renderers),
  493. .controlPoints = std::move (controlPoints),
  494. .children = std::move (children),
  495. .instanceOverride = std::move (instanceOverride),
  496. }
  497. );
  498. } catch (nlohmann::json::exception& e) {
  499. sLog.error ("Error parsing particle '", base.name, "': ", e.what ());
  500. sLog.error ("Particle JSON: ", it.dump ());
  501. throw;
  502. }
  503. }
  504. ParticleEmitter ObjectParser::parseParticleEmitter (const JSON& it) {
  505. std::string name;
  506. const auto nameIt = it.find ("name");
  507. if (nameIt != it.end () && nameIt->is_string ()) {
  508. name = nameIt->get<std::string> ();
  509. }
  510. // vec3 fields may show up as strings, arrays, single numbers, or be missing entirely
  511. auto parseVec3 = [&] (const char* fieldName, const glm::vec3& defaultValue) -> glm::vec3 {
  512. const auto fieldIt = it.find (fieldName);
  513. if (fieldIt == it.end ()) {
  514. return defaultValue;
  515. }
  516. if (fieldIt->is_string ()) {
  517. return it.optional (fieldName, defaultValue);
  518. }
  519. if (fieldIt->is_number ()) {
  520. // single number applies to all components (common for distancemax/distancemin)
  521. float val = fieldIt->get<float> ();
  522. return glm::vec3 (val, val, val);
  523. }
  524. if (fieldIt->is_array () && fieldIt->size () >= 3) {
  525. return glm::vec3 ((*fieldIt)[0].get<float> (), (*fieldIt)[1].get<float> (), (*fieldIt)[2].get<float> ());
  526. }
  527. return defaultValue;
  528. };
  529. auto parseIVec3 = [&] (const char* fieldName, const glm::ivec3& defaultValue) -> glm::ivec3 {
  530. const auto fieldIt = it.find (fieldName);
  531. if (fieldIt == it.end ()) {
  532. return defaultValue;
  533. }
  534. if (fieldIt->is_array () && fieldIt->size () >= 3) {
  535. return glm::ivec3 ((*fieldIt)[0].get<int> (), (*fieldIt)[1].get<int> (), (*fieldIt)[2].get<int> ());
  536. }
  537. return defaultValue;
  538. };
  539. auto parseVec2 = [&] (const char* fieldName, const glm::vec2& defaultValue) -> glm::vec2 {
  540. const auto fieldIt = it.find (fieldName);
  541. if (fieldIt == it.end ()) {
  542. return defaultValue;
  543. }
  544. if (fieldIt->is_string ()) {
  545. return it.optional (fieldName, defaultValue);
  546. }
  547. if (fieldIt->is_array () && fieldIt->size () >= 2) {
  548. return glm::vec2 ((*fieldIt)[0].get<float> (), (*fieldIt)[1].get<float> ());
  549. }
  550. return defaultValue;
  551. };
  552. try {
  553. return ParticleEmitter {
  554. .id = it.optional ("id", -1),
  555. .name = name,
  556. .directions = parseVec3 ("directions", glm::vec3 (1.0f, 1.0f, 0.0f)),
  557. .distanceMin = parseVec3 ("distancemin", glm::vec3 (0.0f, 0.0f, 0.0f)),
  558. .distanceMax = parseVec3 ("distancemax", glm::vec3 (256.0f, 256.0f, 0.0f)),
  559. .origin = parseVec3 ("origin", glm::vec3 (0.0f)),
  560. .sign = parseIVec3 ("sign", glm::ivec3 (0)),
  561. .instantaneous = it.optional ("instantaneous", 0u),
  562. .speedMin = it.optional ("speedmin", 0.0f),
  563. .speedMax = it.optional ("speedmax", 0.0f),
  564. .rate = it.optional ("rate", 10.0f),
  565. .controlPoint = it.optional ("controlpoint", 0),
  566. .flags = it.optional ("flags", 0u),
  567. .cone = it.optional ("cone", 0.0f),
  568. .delay = it.optional ("delay", 0.0f),
  569. .duration = it.optional ("duration", 0.0f),
  570. .audioProcessingBounds = parseVec2 ("audioprocessingbounds", glm::vec2 (0.8f, 1.0f)),
  571. .audioProcessingExponent = it.optional ("audioprocessingexponent", 2.0f),
  572. .audioProcessingFrequencyStart = it.optional ("audioprocessingfrequencystart", 0),
  573. .audioProcessingFrequencyEnd = it.optional ("audioprocessingfrequencyend", 1),
  574. .audioProcessingMode = it.optional ("audioprocessingmode", 0),
  575. .minPeriodicDelay = it.optional ("minperiodicdelay", 1.0f),
  576. .maxPeriodicDelay = it.optional ("maxperiodicdelay", 2.0f),
  577. .minPeriodicDuration = it.optional ("minperiodicduration", 2.0f),
  578. .maxPeriodicDuration = it.optional ("maxperiodicduration", 3.0f),
  579. };
  580. } catch (nlohmann::json::exception& e) {
  581. sLog.error ("Error parsing emitter: ", e.what ());
  582. sLog.error ("Emitter JSON: ", it.dump ());
  583. throw;
  584. }
  585. }
  586. ParticleInitializerUniquePtr ObjectParser::parseParticleInitializer (const JSON& it, const Properties& properties) {
  587. std::string name = it.optional<std::string> ("name", "");
  588. if (name == "colorrandom") {
  589. return std::make_unique<ColorRandomInitializer> (
  590. it.color ("min", properties, Builders::ColorBuilder::Black),
  591. it.color ("max", properties, Builders::ColorBuilder::White)
  592. );
  593. } else if (name == "sizerandom") {
  594. return std::make_unique<SizeRandomInitializer> (
  595. it.user ("min", properties, 0.0f), it.user ("max", properties, 20.0f),
  596. it.user ("exponent", properties, 1.0f)
  597. );
  598. } else if (name == "alpharandom") {
  599. return std::make_unique<AlphaRandomInitializer> (
  600. it.user ("min", properties, 0.05f), it.user ("max", properties, 1.0f)
  601. );
  602. } else if (name == "lifetimerandom") {
  603. return std::make_unique<LifetimeRandomInitializer> (
  604. it.user ("min", properties, 0.0f), it.user ("max", properties, 1.0f)
  605. );
  606. } else if (name == "velocityrandom") {
  607. return std::make_unique<VelocityRandomInitializer> (
  608. it.user ("min", properties, glm::vec3 (-32.0f)), it.user ("max", properties, glm::vec3 (32.0f))
  609. );
  610. } else if (name == "rotationrandom") {
  611. return std::make_unique<RotationRandomInitializer> (
  612. it.user ("min", properties, glm::vec3 (0.0f)),
  613. it.user ("max", properties, glm::vec3 (0.0f, 0.0f, glm::two_pi<float> ()))
  614. );
  615. } else if (name == "angularvelocityrandom") {
  616. return std::make_unique<AngularVelocityRandomInitializer> (
  617. it.user ("min", properties, glm::vec3 (0.0f, 0.0f, -5.0f)),
  618. it.user ("max", properties, glm::vec3 (0.0f, 0.0f, 5.0f)), it.user ("exponent", properties, 1.0f)
  619. );
  620. } else if (name == "turbulentvelocityrandom") {
  621. return std::make_unique<TurbulentVelocityRandomInitializer> (
  622. it.user ("speedmin", properties, 100.0f), it.user ("speedmax", properties, 250.0f),
  623. it.user ("scale", properties, 1.0f), it.user ("offset", properties, 0.0f),
  624. it.user ("forward", properties, glm::vec3 (0.0f, 1.0f, 0.0f)), it.user ("timescale", properties, 1.0f),
  625. it.user ("phasemin", properties, 0.0f), it.user ("phasemax", properties, 0.1f),
  626. it.user ("right", properties, glm::vec3 (0.0f, 0.0f, 1.0f)), it.user ("audioprocessingmode", properties, 0),
  627. it.user ("audioprocessingbounds", properties, glm::vec2 (0.8f, 1.0f)),
  628. it.user ("audioprocessingexponent", properties, 2.0f),
  629. it.user ("audioprocessingfrequencystart", properties, 0),
  630. it.user ("audioprocessingfrequencyend", properties, 1)
  631. );
  632. } else if (name == "mapsequencearoundcontrolpoint") {
  633. return std::make_unique<MapSequenceAroundControlPointInitializer> (
  634. it.user ("controlpoint", properties, 0), it.user ("count", properties, 1),
  635. it.user ("speedmin", properties, glm::vec3 (0.0f)), it.user ("speedmax", properties, glm::vec3 (100.0f))
  636. );
  637. }
  638. return nullptr;
  639. }
  640. ParticleOperatorUniquePtr ObjectParser::parseParticleOperator (const JSON& it, const Properties& properties) {
  641. std::string name = it.optional<std::string> ("name", "");
  642. if (name == "movement") {
  643. return std::make_unique<MovementOperator> (
  644. it.user ("drag", properties, 0.0f), it.user ("gravity", properties, glm::vec3 (0.0f))
  645. );
  646. } else if (name == "angularmovement") {
  647. return std::make_unique<AngularMovementOperator> (
  648. it.user ("drag", properties, 0.0f), it.user ("force", properties, glm::vec3 (0.0f))
  649. );
  650. } else if (name == "alphafade") {
  651. return std::make_unique<AlphaFadeOperator> (
  652. it.user ("fadeintime", properties, 0.5f), it.user ("fadeouttime", properties, 0.5f)
  653. );
  654. } else if (name == "sizechange") {
  655. return std::make_unique<SizeChangeOperator> (
  656. it.user ("starttime", properties, 0.0f), it.user ("endtime", properties, 1.0f),
  657. it.user ("startvalue", properties, 1.0f), it.user ("endvalue", properties, 0.0f)
  658. );
  659. } else if (name == "alphachange") {
  660. return std::make_unique<AlphaChangeOperator> (
  661. it.user ("starttime", properties, 0.0f), it.user ("endtime", properties, 1.0f),
  662. it.user ("startvalue", properties, 1.0f), it.user ("endvalue", properties, 0.0f)
  663. );
  664. } else if (name == "colorchange") {
  665. return std::make_unique<ColorChangeOperator> (
  666. it.user ("starttime", properties, 0.0f), it.user ("endtime", properties, 1.0f),
  667. it.user ("startvalue", properties, glm::vec3 (1.0f)), it.user ("endvalue", properties, glm::vec3 (1.0f))
  668. );
  669. } else if (name == "turbulence") {
  670. return std::make_unique<TurbulenceOperator> (
  671. it.user ("scale", properties, 0.005f), it.user ("speedmin", properties, 500.0f),
  672. it.user ("speedmax", properties, 1000.0f), it.user ("timescale", properties, 0.01f),
  673. it.user ("mask", properties, glm::vec3 (1.0f, 1.0f, 0.0f)), it.user ("phasemin", properties, 0.0f),
  674. it.user ("phasemax", properties, 0.0f), it.user ("audioprocessingmode", properties, 0),
  675. it.user ("audioprocessingbounds", properties, glm::vec2 (0.8f, 1.0f)),
  676. it.user ("audioprocessingexponent", properties, 2.0f),
  677. it.user ("audioprocessingfrequencystart", properties, 0),
  678. it.user ("audioprocessingfrequencyend", properties, 1)
  679. );
  680. } else if (name == "vortex" || name == "vortex_v2") {
  681. return std::make_unique<VortexOperator> (
  682. it.optional ("controlpoint", 0),
  683. it.optional ("flags", 0), // 1 = infinite axis, 2 = maintain distance, 4 = ring shape
  684. it.user ("axis", properties, glm::vec3 (0.0f, 0.0f, 1.0f)),
  685. it.user ("offset", properties, glm::vec3 (0.0f)), it.user ("distanceinner", properties, 500.0f),
  686. it.user ("distanceouter", properties, 650.0f), it.user ("speedinner", properties, 2500.0f),
  687. it.user ("speedouter", properties, 0.0f), it.user ("centerforce", properties, 1.0f),
  688. it.user ("ringradius", properties, 300.0f), it.user ("ringwidth", properties, 50.0f),
  689. it.user ("ringpulldistance", properties, 50.0f), it.user ("ringpullforce", properties, 10.0f),
  690. it.user ("audioprocessingmode", properties, 0),
  691. it.user ("audioprocessingbounds", properties, glm::vec2 (0.8f, 1.0f)),
  692. it.user ("audioprocessingexponent", properties, 2.0f),
  693. it.user ("audioprocessingfrequencystart", properties, 0),
  694. it.user ("audioprocessingfrequencyend", properties, 1)
  695. );
  696. } else if (name == "controlpointattract") {
  697. return std::make_unique<ControlPointAttractOperator> (
  698. it.optional ("controlpoint", 0), it.user ("origin", properties, glm::vec3 (0.0f)),
  699. it.user ("scale", properties, 100.0f), it.user ("threshold", properties, 1000.0f)
  700. );
  701. } else if (name == "oscillatealpha") {
  702. return std::make_unique<OscillateAlphaOperator> (
  703. it.user ("frequencymin", properties, 0.0f), it.user ("frequencymax", properties, 10.0f),
  704. it.user ("scalemin", properties, 0.0f), it.user ("scalemax", properties, 1.0f),
  705. it.user ("phasemin", properties, 0.0f), it.user ("phasemax", properties, glm::two_pi<float> ())
  706. );
  707. } else if (name == "oscillatesize") {
  708. return std::make_unique<OscillateSizeOperator> (
  709. it.user ("frequencymin", properties, 0.0f), it.user ("frequencymax", properties, 10.0f),
  710. it.user ("scalemin", properties, 0.8f), it.user ("scalemax", properties, 1.2f),
  711. it.user ("phasemin", properties, 0.0f), it.user ("phasemax", properties, glm::two_pi<float> ())
  712. );
  713. } else if (name == "oscillateposition") {
  714. return std::make_unique<OscillatePositionOperator> (
  715. it.user ("frequencymin", properties, 0.0f), it.user ("frequencymax", properties, 5.0f),
  716. it.user ("scalemin", properties, 0.0f), it.user ("scalemax", properties, 10.0f),
  717. it.user ("phasemin", properties, 0.0f), it.user ("phasemax", properties, glm::two_pi<float> ()),
  718. it.user ("mask", properties, glm::vec3 (1.0f, 1.0f, 0.0f))
  719. );
  720. }
  721. return nullptr;
  722. }
  723. ParticleRenderer ObjectParser::parseParticleRenderer (const JSON& it) {
  724. std::string name = "sprite";
  725. const auto nameIt = it.find ("name");
  726. if (nameIt != it.end () && nameIt->is_string ()) {
  727. name = nameIt->get<std::string> ();
  728. }
  729. float subdivisionDefault = (name == "rope") ? 4.0f : 1.0f;
  730. float lengthDefault = (name == "ropetrail") ? 1.0f : 0.05f;
  731. return ParticleRenderer {
  732. .name = name,
  733. .length = it.optional ("length", lengthDefault),
  734. .maxLength = it.optional ("maxlength", 10.0f),
  735. .minLength = it.optional ("minlength", 0.0f),
  736. .subdivision = it.optional ("subdivision", subdivisionDefault),
  737. .segments = it.optional ("segments", 4.0f),
  738. .uvScale = it.optional ("uvscale", 1.0f),
  739. .uvScrolling = it.optional ("uvscrolling", false),
  740. .uvSmoothing = it.optional ("uvsmoothing", true),
  741. .fadeAlpha = it.optional ("fadealpha", false),
  742. .fadeSize = it.optional ("fadesize", false),
  743. };
  744. }
  745. ParticleControlPoint ObjectParser::parseParticleControlPoint (const JSON& it) {
  746. // offset can be string "x y z" or array [x,y,z]
  747. glm::vec3 offset (0.0f);
  748. const auto offsetIt = it.find ("offset");
  749. if (offsetIt != it.end ()) {
  750. if (offsetIt->is_string ()) {
  751. std::string offsetStr = offsetIt->get<std::string> ();
  752. std::istringstream iss (offsetStr);
  753. iss >> offset.x >> offset.y >> offset.z;
  754. } else {
  755. try {
  756. offset = it.optional ("offset", glm::vec3 (0.0f));
  757. } catch (...) {
  758. offset = glm::vec3 (0.0f);
  759. }
  760. }
  761. }
  762. return ParticleControlPoint {
  763. .id = it.optional ("id", -1),
  764. .flags = it.optional ("flags", 0u),
  765. .offset = offset,
  766. .lockToPointer = it.optional ("locktopointer", false),
  767. };
  768. }
  769. ParticleChild ObjectParser::parseParticleChild (const JSON& it, const Project& project) {
  770. std::string particleFile = "";
  771. const auto particleIt = it.find ("particle");
  772. if (particleIt != it.end () && particleIt->is_string ()) {
  773. particleFile = particleIt->get<std::string> ();
  774. }
  775. std::string type = "static";
  776. const auto typeIt = it.find ("type");
  777. if (typeIt != it.end () && typeIt->is_string ()) {
  778. type = typeIt->get<std::string> ();
  779. }
  780. std::string name = "";
  781. const auto nameIt = it.find ("name");
  782. if (nameIt != it.end () && nameIt->is_string ()) {
  783. name = nameIt->get<std::string> ();
  784. }
  785. // vec3 fields may show up as strings, arrays, single numbers, or be missing entirely
  786. auto parseVec3 = [&] (const char* fieldName, const glm::vec3& defaultValue) -> glm::vec3 {
  787. const auto fieldIt = it.find (fieldName);
  788. if (fieldIt == it.end ()) {
  789. return defaultValue;
  790. }
  791. if (fieldIt->is_string ()) {
  792. return it.optional (fieldName, defaultValue);
  793. }
  794. if (fieldIt->is_number ()) {
  795. // Single number - use for all components
  796. float val = fieldIt->get<float> ();
  797. return glm::vec3 (val, val, val);
  798. }
  799. if (fieldIt->is_array () && fieldIt->size () >= 3) {
  800. return glm::vec3 ((*fieldIt)[0].get<float> (), (*fieldIt)[1].get<float> (), (*fieldIt)[2].get<float> ());
  801. }
  802. return defaultValue;
  803. };
  804. return ParticleChild {
  805. .type = type,
  806. .name = name,
  807. .maxCount = it.optional ("maxcount", 20),
  808. .controlPointStartIndex = it.optional ("controlpointstartindex", 0),
  809. .probability = it.optional ("probability", 1.0f),
  810. .angles = parseVec3 ("angles", glm::vec3 (0.0f)),
  811. .origin = parseVec3 ("origin", glm::vec3 (0.0f)),
  812. .scale = parseVec3 ("scale", glm::vec3 (1.0f)),
  813. .particleFile = particleFile,
  814. };
  815. }
  816. ParticleInstanceOverride ObjectParser::parseParticleInstanceOverride (const JSON& it, const Properties& properties) {
  817. auto result = ParticleInstanceOverride {
  818. .enabled = it.user ("enabled", properties, true),
  819. .alpha = it.user ("alpha", properties, 1.0f),
  820. .size = it.user ("size", properties, 1.0f),
  821. .lifetime = it.user ("lifetime", properties, 1.0f),
  822. .rate = it.user ("rate", properties, 1.0f),
  823. .speed = it.user ("speed", properties, 1.0f),
  824. .count = it.user ("count", properties, 1.0f),
  825. .color = it.user ("color", properties, glm::vec3 (1.0f)),
  826. .colorn = it.user ("colorn", properties, glm::vec3 (1.0f)),
  827. };
  828. // WE converts the legacy 0-255 "color" into colorn on load (replacing any colorn) and only reads colorn after
  829. if (it.optional ("color").has_value ()) {
  830. result.colorn = Builders::UserSettingBuilder::fromValue (result.color->value->getVec3 () / 255.0f);
  831. }
  832. return result;
  833. }