afxEffectWrapper.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196
  1. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  2. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  3. // Copyright (C) 2015 Faust Logic, Inc.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //
  23. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  24. #include "afx/arcaneFX.h"
  25. #include "math/mathIO.h"
  26. #include "afx/ce/afxComponentEffect.h"
  27. #include "afx/afxResidueMgr.h"
  28. #include "afx/afxChoreographer.h"
  29. #include "afx/afxConstraint.h"
  30. #include "afx/xm/afxXfmMod.h"
  31. #include "afx/afxEffectWrapper.h"
  32. #include "afx/util/afxAnimCurve.h"
  33. #include "afx/util/afxEase.h"
  34. #include "console/typeValidators.h"
  35. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  36. // afxEffectWrapperData
  37. IMPLEMENT_CO_DATABLOCK_V1(afxEffectBaseData);
  38. ConsoleDocClass( afxEffectBaseData,
  39. "@brief A datablock baseclass for afxEffectWrapperData and afxEffectGroupData.\n\n"
  40. "Not intended to be used directly, afxEffectBaseData exists to provide base member "
  41. "variables and generic functionality for the derived classes afxEffectWrapperData and "
  42. "afxEffectGroupData.\n\n"
  43. "@see afxEffectWrapperData\n\n"
  44. "@see afxEffectGroupData\n\n"
  45. "@ingroup afxEffects\n"
  46. "@ingroup AFX\n"
  47. "@ingroup Datablocks\n"
  48. );
  49. IMPLEMENT_CO_DATABLOCK_V1(afxEffectWrapperData);
  50. ConsoleDocClass( afxEffectWrapperData,
  51. "@brief A datablock that describes an Effect Wrapper.\n\n"
  52. "Conceptually an effect wrapper encloses a building-block effect and acts "
  53. "as a handle for adding the effect to a choreographer. Effect wrapper fields "
  54. "primarily deal with effect timing, constraints, and conditional effect execution.\n\n"
  55. "@see afxEffectBaseData\n\n"
  56. "@see afxEffectGroupData\n\n"
  57. "@ingroup afxEffects\n"
  58. "@ingroup AFX\n"
  59. "@ingroup Datablocks\n"
  60. );
  61. afxEffectWrapperData::afxEffectWrapperData()
  62. {
  63. effect_name = ST_NULLSTRING;
  64. effect_data = 0;
  65. effect_desc = 0;
  66. data_ID = 0;
  67. use_as_cons_obj = false;
  68. use_ghost_as_cons_obj = false;
  69. // constraint data
  70. cons_spec = ST_NULLSTRING;
  71. pos_cons_spec = ST_NULLSTRING;
  72. orient_cons_spec = ST_NULLSTRING;
  73. aim_cons_spec = StringTable->insert("camera");
  74. life_cons_spec = ST_NULLSTRING;
  75. // conditional execution flags
  76. effect_enabled = true;
  77. ranking_range.set(0,255);
  78. lod_range.set(0,255);
  79. life_conds = 0;
  80. for (S32 i = 0; i < MAX_CONDITION_STATES; i++)
  81. {
  82. exec_cond_on_bits[i] = 0;
  83. exec_cond_off_bits[i] = 0;
  84. exec_cond_bitmasks[i] = 0;
  85. }
  86. ewd_timing.lifetime = -1;
  87. user_fade_out_time = 0.0;
  88. is_looping = false;
  89. n_loops = 0;
  90. loop_gap_time = 0.0f;
  91. ignore_time_factor = false;
  92. propagate_time_factor = false;
  93. // residue settings
  94. // scaling factors
  95. rate_factor = 1.0f;
  96. scale_factor = 1.0f;
  97. dMemset(xfm_modifiers, 0, sizeof(xfm_modifiers));
  98. forced_bbox.minExtents.set(1,1,1);
  99. forced_bbox.maxExtents.set(-1,-1,-1);
  100. update_forced_bbox = false;
  101. // marked true if datablock ids need to
  102. // be converted into pointers
  103. do_id_convert = false;
  104. sort_priority = 0;
  105. direction.set(0,1,0);
  106. speed = 0.0f;
  107. mass = 1.0f;
  108. borrow_altitudes = false;
  109. vis_keys_spec = ST_NULLSTRING;
  110. vis_keys = 0;
  111. group_index = -1;
  112. inherit_timing = 0;
  113. }
  114. afxEffectWrapperData::afxEffectWrapperData(const afxEffectWrapperData& other, bool temp_clone) : afxEffectBaseData(other, temp_clone)
  115. {
  116. effect_name = other.effect_name;
  117. effect_data = other.effect_data;
  118. effect_desc = other.effect_desc;
  119. data_ID = other.data_ID;
  120. use_as_cons_obj = other.use_as_cons_obj;
  121. use_ghost_as_cons_obj = other.use_ghost_as_cons_obj;
  122. cons_spec = other.cons_spec;
  123. pos_cons_spec = other.pos_cons_spec;
  124. orient_cons_spec = other.orient_cons_spec;
  125. aim_cons_spec = other.aim_cons_spec;
  126. life_cons_spec = other.life_cons_spec;
  127. cons_def = other.cons_def;
  128. pos_cons_def = other.pos_cons_def;
  129. orient_cons_def = other.orient_cons_def;
  130. aim_cons_def = other.aim_cons_def;
  131. life_cons_def = other.life_cons_def;
  132. effect_enabled = other.effect_enabled;
  133. ranking_range = other.ranking_range;
  134. lod_range = other.lod_range;
  135. life_conds = other.life_conds;
  136. dMemcpy(exec_cond_on_bits, other.exec_cond_on_bits, sizeof(exec_cond_on_bits));
  137. dMemcpy(exec_cond_off_bits, other.exec_cond_off_bits, sizeof(exec_cond_off_bits));
  138. dMemcpy(exec_cond_bitmasks, other.exec_cond_bitmasks, sizeof(exec_cond_bitmasks));
  139. ewd_timing = other.ewd_timing;
  140. user_fade_out_time = other.user_fade_out_time;
  141. is_looping = other.is_looping;
  142. n_loops = other.n_loops;
  143. loop_gap_time = other.loop_gap_time;
  144. ignore_time_factor = other.ignore_time_factor;
  145. propagate_time_factor = other.propagate_time_factor;
  146. rate_factor = other.rate_factor;
  147. scale_factor = other.scale_factor;
  148. dMemcpy(xfm_modifiers, other.xfm_modifiers, sizeof(xfm_modifiers));
  149. forced_bbox = other.forced_bbox;
  150. update_forced_bbox = other.update_forced_bbox;
  151. do_id_convert = other.do_id_convert;
  152. sort_priority = other.sort_priority;
  153. direction = other.direction;
  154. speed = other.speed;
  155. mass = other.mass;
  156. borrow_altitudes = other.borrow_altitudes;
  157. vis_keys_spec = other.vis_keys_spec;
  158. vis_keys = other.vis_keys;
  159. if (other.vis_keys)
  160. {
  161. vis_keys = new afxAnimCurve();
  162. for (S32 i = 0; i < other.vis_keys->numKeys(); i++)
  163. {
  164. F32 when = other.vis_keys->getKeyTime(i);
  165. F32 what = other.vis_keys->getKeyValue(i);
  166. vis_keys->addKey(when, what);
  167. }
  168. }
  169. else
  170. vis_keys = 0;
  171. group_index = other.group_index;
  172. inherit_timing = other.inherit_timing;
  173. }
  174. afxEffectWrapperData::~afxEffectWrapperData()
  175. {
  176. if (vis_keys)
  177. delete vis_keys;
  178. }
  179. #define myOffset(field) Offset(field, afxEffectWrapperData)
  180. void afxEffectWrapperData::initPersistFields()
  181. {
  182. docsURL;
  183. // the wrapped effect
  184. addField("effect", TYPEID<SimDataBlock>(), myOffset(effect_data),
  185. "...");
  186. addField("effectName", TypeString, myOffset(effect_name),
  187. "...");
  188. // constraints
  189. addField("constraint", TypeString, myOffset(cons_spec),
  190. "...");
  191. addField("posConstraint", TypeString, myOffset(pos_cons_spec),
  192. "...");
  193. addField("posConstraint2", TypeString, myOffset(aim_cons_spec),
  194. "...");
  195. addField("orientConstraint", TypeString, myOffset(orient_cons_spec),
  196. "...");
  197. addField("lifeConstraint", TypeString, myOffset(life_cons_spec),
  198. "...");
  199. //
  200. addField("isConstraintSrc", TypeBool, myOffset(use_as_cons_obj),
  201. "...");
  202. addField("ghostIsConstraintSrc", TypeBool, myOffset(use_ghost_as_cons_obj),
  203. "...");
  204. addFieldV("delay", TypeRangedF32, myOffset(ewd_timing.delay), &CommonValidators::PositiveFloat,
  205. "...");
  206. addFieldV("lifetime", TypeRangedF32, myOffset(ewd_timing.lifetime), &CommonValidators::PositiveFloat,
  207. "...");
  208. addFieldV("fadeInTime", TypeRangedF32, myOffset(ewd_timing.fade_in_time), &CommonValidators::PositiveFloat,
  209. "...");
  210. addFieldV("residueLifetime", TypeRangedF32, myOffset(ewd_timing.residue_lifetime), &CommonValidators::PositiveFloat,
  211. "...");
  212. addField("fadeInEase", TypePoint2F, myOffset(ewd_timing.fadein_ease),
  213. "...");
  214. addField("fadeOutEase", TypePoint2F, myOffset(ewd_timing.fadeout_ease),
  215. "...");
  216. addFieldV("lifetimeBias", TypeRangedF32, myOffset(ewd_timing.life_bias), &CommonValidators::PositiveFloat,
  217. "...");
  218. addFieldV("fadeOutTime", TypeRangedF32, myOffset(user_fade_out_time), &CommonValidators::PositiveFloat,
  219. "...");
  220. addFieldV("rateFactor", TypeRangedF32, myOffset(rate_factor), &CommonValidators::PositiveFloat,
  221. "...");
  222. addFieldV("scaleFactor", TypeRangedF32, myOffset(scale_factor), &CommonValidators::PositiveFloat,
  223. "...");
  224. addField("isLooping", TypeBool, myOffset(is_looping),
  225. "...");
  226. addFieldV("loopCount", TypeRangedS32, myOffset(n_loops), &CommonValidators::PositiveInt,
  227. "...");
  228. addFieldV("loopGapTime", TypeRangedF32, myOffset(loop_gap_time), &CommonValidators::PositiveFloat,
  229. "...");
  230. addField("ignoreTimeFactor", TypeBool, myOffset(ignore_time_factor),
  231. "...");
  232. addField("propagateTimeFactor", TypeBool, myOffset(propagate_time_factor),
  233. "...");
  234. addField("effectEnabled", TypeBool, myOffset(effect_enabled),
  235. "...");
  236. addField("rankingRange", TypeByteRange, myOffset(ranking_range),
  237. "...");
  238. addField("levelOfDetailRange", TypeByteRange, myOffset(lod_range),
  239. "...");
  240. addField("lifeConditions", TypeS32, myOffset(life_conds),
  241. "...");
  242. addField("execConditions", TypeS32, myOffset(exec_cond_on_bits), MAX_CONDITION_STATES,
  243. "...");
  244. addField("execOffConditions", TypeS32, myOffset(exec_cond_off_bits), MAX_CONDITION_STATES,
  245. "...");
  246. addField("xfmModifiers", TYPEID<afxXM_BaseData>(), myOffset(xfm_modifiers), MAX_XFM_MODIFIERS,
  247. "...");
  248. addField("forcedBBox", TypeBox3F, myOffset(forced_bbox),
  249. "...");
  250. addField("updateForcedBBox", TypeBool, myOffset(update_forced_bbox),
  251. "...");
  252. addField("sortPriority", TypeS8, myOffset(sort_priority),
  253. "...");
  254. addField("direction", TypePoint3F, myOffset(direction),
  255. "...");
  256. addFieldV("speed", TypeRangedF32, myOffset(speed), &CommonValidators::PositiveFloat,
  257. "...");
  258. addFieldV("mass", TypeRangedF32, myOffset(mass), &CommonValidators::PositiveFloat,
  259. "...");
  260. addField("borrowAltitudes", TypeBool, myOffset(borrow_altitudes),
  261. "...");
  262. addField("visibilityKeys", TypeString, myOffset(vis_keys_spec),
  263. "...");
  264. addField("groupIndex", TypeS32, myOffset(group_index),
  265. "...");
  266. addField("inheritGroupTiming", TypeS32, myOffset(inherit_timing),
  267. "...");
  268. Parent::initPersistFields();
  269. // disallow some field substitutions
  270. disableFieldSubstitutions("effect");
  271. onlyKeepClearSubstitutions("xfmModifiers"); // subs resolving to "~~", or "~0" are OK
  272. // Conditional Execution Flags
  273. Con::setIntVariable("$afx::DISABLED", DISABLED);
  274. Con::setIntVariable("$afx::ENABLED", ENABLED);
  275. Con::setIntVariable("$afx::FAILING", FAILING);
  276. Con::setIntVariable("$afx::DEAD", DEAD);
  277. Con::setIntVariable("$afx::ALIVE", ALIVE);
  278. Con::setIntVariable("$afx::DYING", DYING);
  279. }
  280. bool afxEffectWrapperData::onAdd()
  281. {
  282. if (Parent::onAdd() == false)
  283. return false;
  284. if (!effect_data)
  285. {
  286. if (!Sim::findObject((SimObjectId)data_ID, effect_data))
  287. {
  288. Con::errorf("afxEffectWrapperData::onAdd() -- bad datablockId: 0x%x", data_ID);
  289. return false;
  290. }
  291. }
  292. if (effect_data)
  293. {
  294. if (!afxEffectAdapterDesc::identifyEffect(this))
  295. {
  296. Con::errorf("afxEffectWrapperData::onAdd() -- unknown effect type.");
  297. return false;
  298. }
  299. }
  300. parse_cons_specs();
  301. parse_vis_keys();
  302. // figure out if fade-out is for effect of residue
  303. if (ewd_timing.residue_lifetime > 0)
  304. {
  305. ewd_timing.residue_fadetime = user_fade_out_time;
  306. ewd_timing.fade_out_time = 0.0f;
  307. }
  308. else
  309. {
  310. ewd_timing.residue_fadetime = 0.0f;
  311. ewd_timing.fade_out_time = user_fade_out_time;
  312. }
  313. // adjust fade-in time
  314. if (ewd_timing.lifetime >= 0)
  315. {
  316. ewd_timing.fade_in_time = getMin(ewd_timing.lifetime, ewd_timing.fade_in_time);
  317. }
  318. // adjust exec-conditions
  319. for (S32 i = 0; i < MAX_CONDITION_STATES; i++)
  320. exec_cond_bitmasks[i] = exec_cond_on_bits[i] | exec_cond_off_bits[i];
  321. return true;
  322. }
  323. void afxEffectWrapperData::packData(BitStream* stream)
  324. {
  325. Parent::packData(stream);
  326. writeDatablockID(stream, effect_data, mPacked);
  327. stream->writeString(effect_name);
  328. stream->writeString(cons_spec);
  329. stream->writeString(pos_cons_spec);
  330. stream->writeString(orient_cons_spec);
  331. stream->writeString(aim_cons_spec);
  332. stream->writeString(life_cons_spec);
  333. //
  334. stream->write(use_as_cons_obj);
  335. //stream->write(use_ghost_as_cons_obj);
  336. stream->writeFlag(effect_enabled);
  337. stream->write(ranking_range.low);
  338. stream->write(ranking_range.high);
  339. stream->write(lod_range.low);
  340. stream->write(lod_range.high);
  341. for (S32 i = 0; i < MAX_CONDITION_STATES; i++)
  342. {
  343. stream->write(exec_cond_on_bits[i]);
  344. stream->write(exec_cond_off_bits[i]);
  345. }
  346. stream->write(life_conds);
  347. stream->write(ewd_timing.delay);
  348. stream->write(ewd_timing.lifetime);
  349. stream->write(ewd_timing.fade_in_time);
  350. stream->write(user_fade_out_time);
  351. stream->write(is_looping);
  352. stream->write(n_loops);
  353. stream->write(loop_gap_time);
  354. stream->write(ignore_time_factor);
  355. stream->write(propagate_time_factor);
  356. stream->write(ewd_timing.residue_lifetime);
  357. stream->write(rate_factor);
  358. stream->write(scale_factor);
  359. // modifiers
  360. pack_mods(stream, xfm_modifiers, mPacked);
  361. mathWrite(*stream, forced_bbox);
  362. stream->write(update_forced_bbox);
  363. stream->write(sort_priority);
  364. mathWrite(*stream, direction);
  365. stream->write(speed);
  366. stream->write(mass);
  367. stream->write(borrow_altitudes);
  368. if (stream->writeFlag(vis_keys_spec != ST_NULLSTRING))
  369. stream->writeLongString(1023, vis_keys_spec);
  370. if (stream->writeFlag(group_index != -1))
  371. stream->write(group_index);
  372. stream->writeInt(inherit_timing, TIMING_BITS);
  373. }
  374. void afxEffectWrapperData::unpackData(BitStream* stream)
  375. {
  376. Parent::unpackData(stream);
  377. data_ID = readDatablockID(stream);
  378. effect_name = stream->readSTString();
  379. cons_spec = stream->readSTString();
  380. pos_cons_spec = stream->readSTString();
  381. orient_cons_spec = stream->readSTString();
  382. aim_cons_spec = stream->readSTString();
  383. life_cons_spec = stream->readSTString();
  384. //
  385. stream->read(&use_as_cons_obj);
  386. //stream->read(&use_ghost_as_cons_obj);
  387. effect_enabled = stream->readFlag();
  388. stream->read(&ranking_range.low);
  389. stream->read(&ranking_range.high);
  390. stream->read(&lod_range.low);
  391. stream->read(&lod_range.high);
  392. for (S32 i = 0; i < MAX_CONDITION_STATES; i++)
  393. {
  394. stream->read(&exec_cond_on_bits[i]);
  395. stream->read(&exec_cond_off_bits[i]);
  396. }
  397. stream->read(&life_conds);
  398. stream->read(&ewd_timing.delay);
  399. stream->read(&ewd_timing.lifetime);
  400. stream->read(&ewd_timing.fade_in_time);
  401. stream->read(&user_fade_out_time);
  402. stream->read(&is_looping);
  403. stream->read(&n_loops);
  404. stream->read(&loop_gap_time);
  405. stream->read(&ignore_time_factor);
  406. stream->read(&propagate_time_factor);
  407. stream->read(&ewd_timing.residue_lifetime);
  408. stream->read(&rate_factor);
  409. stream->read(&scale_factor);
  410. // modifiers
  411. do_id_convert = true;
  412. unpack_mods(stream, xfm_modifiers);
  413. mathRead(*stream, &forced_bbox);
  414. stream->read(&update_forced_bbox);
  415. stream->read(&sort_priority);
  416. mathRead(*stream, &direction);
  417. stream->read(&speed);
  418. stream->read(&mass);
  419. stream->read(&borrow_altitudes);
  420. if (stream->readFlag())
  421. {
  422. char buf[1024];
  423. stream->readLongString(1023, buf);
  424. vis_keys_spec = StringTable->insert(buf);
  425. }
  426. else
  427. vis_keys_spec = ST_NULLSTRING;
  428. if (stream->readFlag())
  429. stream->read(&group_index);
  430. else
  431. group_index = -1;
  432. inherit_timing = stream->readInt(TIMING_BITS);
  433. }
  434. /* static*/
  435. S32 num_modifiers(afxXM_BaseData* mods[])
  436. {
  437. S32 n_mods = 0;
  438. for (int i = 0; i < afxEffectDefs::MAX_XFM_MODIFIERS; i++)
  439. {
  440. if (mods[i])
  441. {
  442. if (i != n_mods)
  443. {
  444. mods[n_mods] = mods[i];
  445. mods[i] = 0;
  446. }
  447. n_mods++;
  448. }
  449. }
  450. return n_mods;
  451. }
  452. void afxEffectWrapperData::parse_cons_specs()
  453. {
  454. // parse the constraint specifications
  455. bool runs_on_s = runsOnServer();
  456. bool runs_on_c = runsOnClient();
  457. cons_def.parseSpec(cons_spec, runs_on_s, runs_on_c);
  458. pos_cons_def.parseSpec(pos_cons_spec, runs_on_s, runs_on_c);
  459. orient_cons_def.parseSpec(orient_cons_spec, runs_on_s, runs_on_c);
  460. aim_cons_def.parseSpec(aim_cons_spec, runs_on_s, runs_on_c);
  461. life_cons_def.parseSpec(life_cons_spec, runs_on_s, runs_on_c);
  462. if (cons_def.isDefined())
  463. {
  464. pos_cons_def = cons_def;
  465. if (!orient_cons_def.isDefined())
  466. orient_cons_def = cons_def;
  467. }
  468. }
  469. void afxEffectWrapperData::parse_vis_keys()
  470. {
  471. if (vis_keys_spec != ST_NULLSTRING)
  472. {
  473. if (vis_keys)
  474. delete vis_keys;
  475. vis_keys = new afxAnimCurve();
  476. char* keys_buffer = dStrdup(vis_keys_spec);
  477. char* key_token = dStrtok(keys_buffer, " \t");
  478. while (key_token != NULL)
  479. {
  480. char* colon = dStrchr(key_token, ':');
  481. if (colon)
  482. {
  483. *colon = '\0';
  484. F32 when = dAtof(key_token);
  485. F32 what = dAtof(colon+1);
  486. vis_keys->addKey(when, what);
  487. }
  488. key_token = dStrtok(NULL, " \t");
  489. }
  490. dFree(keys_buffer);
  491. vis_keys->sort();
  492. }
  493. }
  494. void afxEffectWrapperData::gather_cons_defs(Vector<afxConstraintDef>& defs)
  495. {
  496. if (pos_cons_def.isDefined())
  497. defs.push_back(pos_cons_def);
  498. if (orient_cons_def.isDefined())
  499. defs.push_back(orient_cons_def);
  500. if (aim_cons_def.isDefined())
  501. defs.push_back(aim_cons_def);
  502. if (life_cons_def.isDefined())
  503. defs.push_back(life_cons_def);
  504. afxComponentEffectData* ce_data = dynamic_cast<afxComponentEffectData*>(effect_data);
  505. if (ce_data)
  506. ce_data->gather_cons_defs(defs);
  507. }
  508. void afxEffectWrapperData::pack_mods(BitStream* stream, afxXM_BaseData* mods[], bool packed)
  509. {
  510. S32 n_mods = num_modifiers(mods);
  511. stream->writeInt(n_mods, 6);
  512. for (int i = 0; i < n_mods; i++)
  513. writeDatablockID(stream, mods[i], packed);
  514. }
  515. void afxEffectWrapperData::unpack_mods(BitStream* stream, afxXM_BaseData* mods[])
  516. {
  517. S32 n_mods = stream->readInt(6);
  518. for (int i = 0; i < n_mods; i++)
  519. mods[i] = (afxXM_BaseData*)(uintptr_t)readDatablockID(stream);
  520. }
  521. bool afxEffectWrapperData::preload(bool server, String &errorStr)
  522. {
  523. if (!Parent::preload(server, errorStr))
  524. return false;
  525. // Resolve objects transmitted from server
  526. if (!server)
  527. {
  528. if (do_id_convert)
  529. {
  530. for (int i = 0; i < MAX_XFM_MODIFIERS; i++)
  531. {
  532. SimObjectId db_id = SimObjectId((uintptr_t)xfm_modifiers[i]);
  533. if (db_id != 0)
  534. {
  535. // try to convert id to pointer
  536. if (!Sim::findObject(db_id, xfm_modifiers[i]))
  537. {
  538. Con::errorf("afxEffectWrapperData::preload() -- bad datablockId: 0x%x (xfm_modifiers[%d])",
  539. db_id, i);
  540. }
  541. }
  542. do_id_convert = false;
  543. }
  544. }
  545. }
  546. return true;
  547. }
  548. void afxEffectWrapperData::onPerformSubstitutions()
  549. {
  550. Parent::onPerformSubstitutions();
  551. parse_cons_specs();
  552. parse_vis_keys();
  553. if (ewd_timing.residue_lifetime > 0)
  554. {
  555. ewd_timing.residue_fadetime = user_fade_out_time;
  556. ewd_timing.fade_out_time = 0.0f;
  557. }
  558. else
  559. {
  560. ewd_timing.residue_fadetime = 0.0f;
  561. ewd_timing.fade_out_time = user_fade_out_time;
  562. }
  563. }
  564. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  565. // afxEffectWrapper
  566. IMPLEMENT_CONOBJECT(afxEffectWrapper);
  567. ConsoleDocClass( afxEffectWrapper,
  568. "@brief An Effect Wrapper as defined by an afxEffectWrapperData datablock.\n\n"
  569. "Conceptually an effect wrapper encloses a building-block effect and acts "
  570. "as a handle for adding the effect to a choreographer. Effect wrapper fields "
  571. "primarily deal with effect timing, constraints, and conditional effect execution.\n\n"
  572. "Not intended to be used directly, afxEffectWrapper is an internal baseclass used to "
  573. "implement effect-specific adapter classes.\n\n"
  574. "@ingroup afxEffects\n"
  575. "@ingroup AFX\n"
  576. );
  577. afxEffectWrapper::afxEffectWrapper()
  578. {
  579. mChoreographer = 0;
  580. mDatablock = 0;
  581. mCons_mgr = 0;
  582. mCond_alive = true;
  583. mElapsed = 0;
  584. mLife_end = 0;
  585. mLife_elapsed = 0;
  586. mStopped = false;
  587. mNum_updates = 0;
  588. mFade_value = 1.0f;
  589. mLast_fade_value = 0.0f;
  590. mFade_in_end = 0.0;
  591. mFade_out_start = 0.0f;
  592. mIn_scope = true;
  593. mIs_aborted = false;
  594. mDo_fade_inout = false;
  595. mDo_fades = false;
  596. mFull_lifetime = 0;
  597. mTime_factor = 1.0f;
  598. mProp_time_factor = 1.0f;
  599. mLive_scale_factor = 1.0f;
  600. mLive_fade_factor = 1.0f;
  601. mTerrain_altitude = -1.0f;
  602. mInterior_altitude = -1.0f;
  603. mGroup_index = 0;
  604. dMemset(mXfm_modifiers, 0, sizeof(mXfm_modifiers));
  605. }
  606. afxEffectWrapper::~afxEffectWrapper()
  607. {
  608. for (S32 i = 0; i < MAX_XFM_MODIFIERS; i++)
  609. if (mXfm_modifiers[i])
  610. delete mXfm_modifiers[i];
  611. if (mDatablock && mDatablock->effect_name != ST_NULLSTRING)
  612. {
  613. mChoreographer->removeNamedEffect(this);
  614. if (mDatablock->use_as_cons_obj && !mEffect_cons_id.undefined())
  615. mCons_mgr->setReferenceEffect(mEffect_cons_id, 0);
  616. }
  617. if (mDatablock && mDatablock->isTempClone())
  618. delete mDatablock;
  619. mDatablock = 0;
  620. }
  621. #undef myOffset
  622. #define myOffset(field) Offset(field, afxEffectWrapper)
  623. void afxEffectWrapper::initPersistFields()
  624. {
  625. docsURL;
  626. addFieldV("liveScaleFactor", TypeRangedF32, myOffset(mLive_scale_factor), &CommonValidators::PositiveFloat,
  627. "...");
  628. addFieldV("liveFadeFactor", TypeRangedF32, myOffset(mLive_fade_factor), &CommonValidators::PositiveFloat,
  629. "...");
  630. Parent::initPersistFields();
  631. }
  632. void afxEffectWrapper::ew_init(afxChoreographer* choreographer,
  633. afxEffectWrapperData* datablock,
  634. afxConstraintMgr* cons_mgr,
  635. F32 time_factor)
  636. {
  637. AssertFatal(choreographer != NULL, "Choreographer is missing.");
  638. AssertFatal(datablock != NULL, "Datablock is missing.");
  639. AssertFatal(cons_mgr != NULL, "Constraint manager is missing.");
  640. mChoreographer = choreographer;
  641. mDatablock = datablock;
  642. mCons_mgr = cons_mgr;
  643. ea_set_datablock(datablock->effect_data);
  644. mEW_timing = datablock->ewd_timing;
  645. if (mEW_timing.life_bias != 1.0f)
  646. {
  647. if (mEW_timing.lifetime > 0)
  648. mEW_timing.lifetime *= mEW_timing.life_bias;
  649. mEW_timing.fade_in_time *= mEW_timing.life_bias;
  650. mEW_timing.fade_out_time *= mEW_timing.life_bias;
  651. }
  652. mPos_cons_id = cons_mgr->getConstraintId(datablock->pos_cons_def);
  653. mOrient_cons_id = cons_mgr->getConstraintId(datablock->orient_cons_def);
  654. mAim_cons_id = cons_mgr->getConstraintId(datablock->aim_cons_def);
  655. mLife_cons_id = cons_mgr->getConstraintId(datablock->life_cons_def);
  656. mTime_factor = (datablock->ignore_time_factor) ? 1.0f : time_factor;
  657. if (datablock->propagate_time_factor)
  658. mProp_time_factor = time_factor;
  659. if (datablock->runsHere(choreographer->isServerObject()))
  660. {
  661. for (int i = 0; i < MAX_XFM_MODIFIERS && datablock->xfm_modifiers[i] != 0; i++)
  662. {
  663. mXfm_modifiers[i] = datablock->xfm_modifiers[i]->create(this, choreographer->isServerObject());
  664. AssertFatal(mXfm_modifiers[i] != 0, avar("Error, creation failed for xfm_modifiers[%d] of %s.", i, datablock->getName()));
  665. if (mXfm_modifiers[i] == 0)
  666. Con::errorf("Error, creation failed for xfm_modifiers[%d] of %s.", i, datablock->getName());
  667. }
  668. }
  669. if (datablock->effect_name != ST_NULLSTRING)
  670. {
  671. assignName(datablock->effect_name);
  672. choreographer->addNamedEffect(this);
  673. if (datablock->use_as_cons_obj)
  674. {
  675. mEffect_cons_id = cons_mgr->setReferenceEffect(datablock->effect_name, this);
  676. if (mEffect_cons_id.undefined() && datablock->isTempClone() && datablock->runsHere(choreographer->isServerObject()))
  677. mEffect_cons_id = cons_mgr->createReferenceEffect(datablock->effect_name, this);
  678. }
  679. }
  680. }
  681. void afxEffectWrapper::prestart()
  682. {
  683. // modify timing values by time_factor
  684. if (mEW_timing.lifetime > 0)
  685. mEW_timing.lifetime *= mTime_factor;
  686. mEW_timing.delay *= mTime_factor;
  687. mEW_timing.fade_in_time *= mTime_factor;
  688. mEW_timing.fade_out_time *= mTime_factor;
  689. if (mEW_timing.lifetime < 0)
  690. {
  691. mFull_lifetime = INFINITE_LIFETIME;
  692. mLife_end = INFINITE_LIFETIME;
  693. }
  694. else
  695. {
  696. mFull_lifetime = mEW_timing.lifetime + mEW_timing.fade_out_time;
  697. mLife_end = mEW_timing.delay + mEW_timing.lifetime;
  698. }
  699. if ((mEW_timing.fade_in_time + mEW_timing.fade_out_time) > 0.0f)
  700. {
  701. mFade_in_end = mEW_timing.delay + mEW_timing.fade_in_time;
  702. if (mFull_lifetime == (F32)INFINITE_LIFETIME)
  703. mFade_out_start = (F32)INFINITE_LIFETIME;
  704. else
  705. mFade_out_start = mEW_timing.delay + mEW_timing.lifetime;
  706. mDo_fade_inout = true;
  707. }
  708. if (!mDo_fade_inout && mDatablock->vis_keys != NULL && mDatablock->vis_keys->numKeys() > 0)
  709. {
  710. //do_fades = true;
  711. mFade_out_start = mEW_timing.delay + mEW_timing.lifetime;
  712. }
  713. }
  714. bool afxEffectWrapper::start(F32 timestamp)
  715. {
  716. if (!ea_is_enabled())
  717. {
  718. Con::warnf("afxEffectWrapper::start() -- effect type of %s is currently disabled.", mDatablock->getName());
  719. return false;
  720. }
  721. afxConstraint* life_constraint = getLifeConstraint();
  722. if (life_constraint)
  723. mCond_alive = life_constraint->getLivingState();
  724. mElapsed = timestamp;
  725. for (S32 i = 0; i < MAX_XFM_MODIFIERS; i++)
  726. {
  727. if (!mXfm_modifiers[i])
  728. break;
  729. else
  730. mXfm_modifiers[i]->start(timestamp);
  731. }
  732. if (!ea_start())
  733. {
  734. // subclass should print error message if applicable, so no message here.
  735. return false;
  736. }
  737. update(0.0f);
  738. return !isAborted();
  739. }
  740. bool afxEffectWrapper::test_life_conds()
  741. {
  742. afxConstraint* life_constraint = getLifeConstraint();
  743. if (!life_constraint || mDatablock->life_conds == 0)
  744. return true;
  745. S32 now_state = life_constraint->getDamageState();
  746. if ((mDatablock->life_conds & DEAD) != 0 && now_state == ShapeBase::Disabled)
  747. return true;
  748. if ((mDatablock->life_conds & ALIVE) != 0 && now_state == ShapeBase::Enabled)
  749. return true;
  750. if ((mDatablock->life_conds & DYING) != 0)
  751. return (mCond_alive && now_state == ShapeBase::Disabled);
  752. return false;
  753. }
  754. bool afxEffectWrapper::update(F32 dt)
  755. {
  756. mElapsed += dt;
  757. // life_elapsed won't exceed full_lifetime
  758. mLife_elapsed = getMin(mElapsed - mEW_timing.delay, mFull_lifetime);
  759. // update() returns early if elapsed is outside of active timing range
  760. // (delay <= elapsed <= delay+lifetime)
  761. // note: execution is always allowed beyond this point at least once,
  762. // even if elapsed exceeds the lifetime.
  763. if (mElapsed < mEW_timing.delay)
  764. {
  765. setScopeStatus(false);
  766. return false;
  767. }
  768. if (!mDatablock->requiresStop(mEW_timing) && mEW_timing.lifetime < 0)
  769. {
  770. F32 afterlife = mElapsed - mEW_timing.delay;
  771. if (afterlife > 1.0f || ((afterlife > 0.0f) && (mNum_updates > 0)))
  772. {
  773. setScopeStatus(mEW_timing.residue_lifetime > 0.0f);
  774. return false;
  775. }
  776. }
  777. else
  778. {
  779. F32 afterlife = mElapsed - (mFull_lifetime + mEW_timing.delay);
  780. if (afterlife > 1.0f || ((afterlife > 0.0f) && (mNum_updates > 0)))
  781. {
  782. setScopeStatus(mEW_timing.residue_lifetime > 0.0f);
  783. return false;
  784. }
  785. }
  786. // first time here, test if required conditions for effect are met
  787. if (mNum_updates == 0)
  788. {
  789. if (!test_life_conds())
  790. {
  791. mElapsed = mFull_lifetime + mEW_timing.delay;
  792. setScopeStatus(false);
  793. mNum_updates++;
  794. return false;
  795. }
  796. }
  797. setScopeStatus(true);
  798. mNum_updates++;
  799. // calculate current fade value if enabled
  800. if (mDo_fade_inout)
  801. {
  802. if (mEW_timing.fade_in_time > 0 && mElapsed <= mFade_in_end)
  803. {
  804. F32 t = mClampF((mElapsed - mEW_timing.delay)/ mEW_timing.fade_in_time, 0.0f, 1.0f);
  805. mFade_value = afxEase::t(t, mEW_timing.fadein_ease.x, mEW_timing.fadein_ease.y);
  806. mDo_fades = true;
  807. }
  808. else if (mElapsed > mFade_out_start)
  809. {
  810. if (mEW_timing.fade_out_time == 0)
  811. mFade_value = 0.0f;
  812. else
  813. {
  814. F32 t = mClampF(1.0f-(mElapsed - mFade_out_start)/ mEW_timing.fade_out_time, 0.0f, 1.0f);
  815. mFade_value = afxEase::t(t, mEW_timing.fadeout_ease.x, mEW_timing.fadeout_ease.y);
  816. }
  817. mDo_fades = true;
  818. }
  819. else
  820. {
  821. mFade_value = 1.0f;
  822. mDo_fades = false;
  823. }
  824. }
  825. else
  826. {
  827. mFade_value = 1.0;
  828. mDo_fades = false;
  829. }
  830. if (mDatablock->vis_keys && mDatablock->vis_keys->numKeys() > 0)
  831. {
  832. F32 vis = mDatablock->vis_keys->evaluate(mElapsed - mEW_timing.delay);
  833. mFade_value *= mClampF(vis, 0.0f, 1.0f);
  834. mDo_fades = (mFade_value < 1.0f);
  835. }
  836. // DEAL WITH CONSTRAINTS
  837. afxXM_Params params;
  838. Point3F& CONS_POS = params.pos;
  839. MatrixF& CONS_XFM = params.ori;
  840. Point3F& CONS_AIM = params.pos2;
  841. Point3F& CONS_SCALE = params.scale;
  842. LinearColorF& CONS_COLOR = params.color;
  843. afxConstraint* pos_constraint = getPosConstraint();
  844. if (pos_constraint)
  845. {
  846. bool valid = pos_constraint->getPosition(CONS_POS, mDatablock->pos_cons_def.mHistory_time);
  847. if (!valid)
  848. getUnconstrainedPosition(CONS_POS);
  849. setScopeStatus(valid);
  850. if (valid && mDatablock->borrow_altitudes)
  851. {
  852. F32 terr_alt, inter_alt;
  853. if (pos_constraint->getAltitudes(terr_alt, inter_alt))
  854. {
  855. mTerrain_altitude = terr_alt;
  856. mInterior_altitude = inter_alt;
  857. }
  858. }
  859. }
  860. else
  861. {
  862. getUnconstrainedPosition(CONS_POS);
  863. setScopeStatus(false);
  864. }
  865. afxConstraint* orient_constraint = getOrientConstraint();
  866. if (orient_constraint)
  867. {
  868. orient_constraint->getTransform(CONS_XFM, mDatablock->pos_cons_def.mHistory_time);
  869. }
  870. else
  871. {
  872. getUnconstrainedTransform(CONS_XFM);
  873. }
  874. afxConstraint* aim_constraint = getAimConstraint();
  875. if (aim_constraint)
  876. aim_constraint->getPosition(CONS_AIM, mDatablock->pos_cons_def.mHistory_time);
  877. else
  878. CONS_AIM.zero();
  879. CONS_SCALE.set(mDatablock->scale_factor, mDatablock->scale_factor, mDatablock->scale_factor);
  880. /*
  881. if (datablock->isPositional() && CONS_POS.isZero() && in_scope)
  882. Con::errorf("#EFFECT AT ORIGIN [%s] time=%g", datablock->getName(), dt);
  883. */
  884. getBaseColor(CONS_COLOR);
  885. params.vis = mFade_value;
  886. // apply modifiers
  887. for (int i = 0; i < MAX_XFM_MODIFIERS; i++)
  888. {
  889. if (!mXfm_modifiers[i])
  890. break;
  891. else
  892. mXfm_modifiers[i]->updateParams(dt, mLife_elapsed, params);
  893. }
  894. // final pos/orient is determined
  895. mUpdated_xfm = CONS_XFM;
  896. mUpdated_pos = CONS_POS;
  897. mUpdated_aim = CONS_AIM;
  898. mUpdated_xfm.setPosition(mUpdated_pos);
  899. mUpdated_scale = CONS_SCALE;
  900. mUpdated_color = CONS_COLOR;
  901. if (params.vis > 1.0f)
  902. mFade_value = 1.0f;
  903. else
  904. mFade_value = params.vis;
  905. if (mLast_fade_value != mFade_value)
  906. {
  907. mDo_fades = true;
  908. mLast_fade_value = mFade_value;
  909. }
  910. else
  911. {
  912. mDo_fades = (mFade_value < 1.0f);
  913. }
  914. if (!ea_update(dt))
  915. {
  916. mIs_aborted = true;
  917. Con::errorf("afxEffectWrapper::update() -- effect %s ended unexpectedly.", mDatablock->getName());
  918. }
  919. return true;
  920. }
  921. void afxEffectWrapper::stop()
  922. {
  923. if (!mDatablock->requiresStop(mEW_timing))
  924. return;
  925. mStopped = true;
  926. // this resets full_lifetime so it starts to shrink or fade
  927. if (mFull_lifetime == (F32)INFINITE_LIFETIME)
  928. {
  929. mFull_lifetime = (mElapsed - mEW_timing.delay) + afterStopTime();
  930. mLife_end = mElapsed;
  931. if (mEW_timing.fade_out_time > 0)
  932. mFade_out_start = mElapsed;
  933. }
  934. }
  935. void afxEffectWrapper::cleanup(bool was_stopped)
  936. {
  937. ea_finish(was_stopped);
  938. if (!mEffect_cons_id.undefined())
  939. {
  940. mCons_mgr->setReferenceEffect(mEffect_cons_id, 0);
  941. mEffect_cons_id = afxConstraintID();
  942. }
  943. }
  944. void afxEffectWrapper::setScopeStatus(bool in_scope)
  945. {
  946. if (mIn_scope != in_scope)
  947. {
  948. mIn_scope = in_scope;
  949. ea_set_scope_status(in_scope);
  950. }
  951. }
  952. bool afxEffectWrapper::isDone()
  953. {
  954. if (!mDatablock->is_looping)
  955. return (mElapsed >= (mLife_end + mEW_timing.fade_out_time));
  956. return false;
  957. }
  958. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  959. // static
  960. afxEffectWrapper* afxEffectWrapper::ew_create(afxChoreographer* choreographer,
  961. afxEffectWrapperData* datablock,
  962. afxConstraintMgr* cons_mgr,
  963. F32 time_factor,
  964. S32 group_index)
  965. {
  966. afxEffectWrapper* adapter = datablock->effect_desc->create();
  967. if (adapter)
  968. {
  969. adapter->mGroup_index = (datablock->group_index != -1) ? datablock->group_index : group_index;
  970. adapter->ew_init(choreographer, datablock, cons_mgr, time_factor);
  971. }
  972. return adapter;
  973. }
  974. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  975. Vector<afxEffectAdapterDesc*>* afxEffectAdapterDesc::adapters = 0;
  976. afxEffectAdapterDesc::afxEffectAdapterDesc()
  977. {
  978. if (!adapters)
  979. adapters = new Vector<afxEffectAdapterDesc*>;
  980. adapters->push_back(this);
  981. }
  982. bool afxEffectAdapterDesc::identifyEffect(afxEffectWrapperData* ew)
  983. {
  984. if (!ew || !ew->effect_data)
  985. {
  986. Con::errorf("afxEffectAdapterDesc::identifyEffect() -- effect datablock was not specified.");
  987. return false;
  988. }
  989. if (!adapters)
  990. {
  991. Con::errorf("afxEffectAdapterDesc::identifyEffect() -- adapter registration list has not been allocated.");
  992. return false;
  993. }
  994. if (adapters->size() == 0)
  995. {
  996. Con::errorf("afxEffectAdapterDesc::identifyEffect() -- no effect adapters have been registered.");
  997. return false;
  998. }
  999. for (S32 i = 0; i < adapters->size(); i++)
  1000. {
  1001. if ((*adapters)[i]->testEffectType(ew->effect_data))
  1002. {
  1003. ew->effect_desc = (*adapters)[i];
  1004. (*adapters)[i]->prepEffect(ew);
  1005. return true;
  1006. }
  1007. }
  1008. Con::errorf("afxEffectAdapterDesc::identifyEffect() -- effect %s has an undefined type. -- %d",
  1009. ew->effect_data->getName(), adapters->size());
  1010. return false;
  1011. }
  1012. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//