You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
16KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  3. static float_4 shapeDelta(float_4 delta, float_4 tau, float shape) {
  4. float_4 lin = simd::sgn(delta) * 10.f / tau;
  5. if (shape < 0.f) {
  6. float_4 log = simd::sgn(delta) * 40.f / tau / (simd::fabs(delta) + 1.f);
  7. return simd::crossfade(lin, log, -shape * 0.95f);
  8. }
  9. else {
  10. float_4 exp = M_E * delta / tau;
  11. return simd::crossfade(lin, exp, shape * 0.90f);
  12. }
  13. }
  14. struct Rampage : Module {
  15. enum ParamIds {
  16. RANGE_A_PARAM,
  17. RANGE_B_PARAM,
  18. SHAPE_A_PARAM,
  19. SHAPE_B_PARAM,
  20. TRIGG_A_PARAM,
  21. TRIGG_B_PARAM,
  22. RISE_A_PARAM,
  23. RISE_B_PARAM,
  24. FALL_A_PARAM,
  25. FALL_B_PARAM,
  26. CYCLE_A_PARAM,
  27. CYCLE_B_PARAM,
  28. BALANCE_PARAM,
  29. NUM_PARAMS
  30. };
  31. enum InputIds {
  32. IN_A_INPUT,
  33. IN_B_INPUT,
  34. TRIGG_A_INPUT,
  35. TRIGG_B_INPUT,
  36. RISE_CV_A_INPUT,
  37. RISE_CV_B_INPUT,
  38. FALL_CV_A_INPUT,
  39. FALL_CV_B_INPUT,
  40. EXP_CV_A_INPUT,
  41. EXP_CV_B_INPUT,
  42. CYCLE_A_INPUT,
  43. CYCLE_B_INPUT,
  44. NUM_INPUTS
  45. };
  46. enum OutputIds {
  47. RISING_A_OUTPUT,
  48. RISING_B_OUTPUT,
  49. FALLING_A_OUTPUT,
  50. FALLING_B_OUTPUT,
  51. EOC_A_OUTPUT,
  52. EOC_B_OUTPUT,
  53. OUT_A_OUTPUT,
  54. OUT_B_OUTPUT,
  55. COMPARATOR_OUTPUT,
  56. MIN_OUTPUT,
  57. MAX_OUTPUT,
  58. NUM_OUTPUTS
  59. };
  60. enum LightIds {
  61. ENUMS(COMPARATOR_LIGHT, 3),
  62. ENUMS(MIN_LIGHT, 3),
  63. ENUMS(MAX_LIGHT, 3),
  64. ENUMS(OUT_A_LIGHT, 3),
  65. ENUMS(OUT_B_LIGHT, 3),
  66. ENUMS(RISING_A_LIGHT, 3),
  67. ENUMS(RISING_B_LIGHT, 3),
  68. ENUMS(FALLING_A_LIGHT, 3),
  69. ENUMS(FALLING_B_LIGHT, 3),
  70. NUM_LIGHTS
  71. };
  72. float_4 out[2][4] = {};
  73. float_4 gate[2][4] = {}; // use simd __m128 logic instead of bool
  74. dsp::TSchmittTrigger<float_4> trigger_4[2][4];
  75. PulseGenerator_4 endOfCyclePulse[2][4];
  76. // ChannelMask channelMask;
  77. Rampage() {
  78. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  79. configSwitch(RANGE_A_PARAM, 0.0, 2.0, 0.0, "Ch 1 range", {"Medium", "Fast", "Slow"});
  80. configParam(SHAPE_A_PARAM, -1.0, 1.0, 0.0, "Ch 1 shape");
  81. configButton(TRIGG_A_PARAM, "Ch 1 trigger");
  82. configParam(RISE_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 rise time");
  83. configParam(FALL_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 fall time");
  84. configSwitch(CYCLE_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 cycle", {"Off", "On"});
  85. configSwitch(RANGE_B_PARAM, 0.0, 2.0, 0.0, "Ch 2 range", {"Medium", "Fast", "Slow"});
  86. configParam(SHAPE_B_PARAM, -1.0, 1.0, 0.0, "Ch 2 shape");
  87. configButton(TRIGG_B_PARAM, "Ch 2 trigger");
  88. configParam(RISE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 rise time");
  89. configParam(FALL_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 fall time");
  90. configSwitch(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle", {"Off", "On"});
  91. configParam(BALANCE_PARAM, 0.0, 1.0, 0.5, "Balance");
  92. configInput(IN_A_INPUT, "A");
  93. configInput(IN_B_INPUT, "B");
  94. configInput(TRIGG_A_INPUT, "Trigger A");
  95. configInput(TRIGG_B_INPUT, "Trigger B");
  96. configInput(RISE_CV_A_INPUT, "Rise CV A");
  97. configInput(RISE_CV_B_INPUT, "Rise CV B");
  98. configInput(FALL_CV_A_INPUT, "Fall CV A");
  99. configInput(FALL_CV_B_INPUT, "Fall CV B");
  100. configInput(EXP_CV_A_INPUT, "Exponetial CV A");
  101. configInput(EXP_CV_B_INPUT, "Exponetial CV B");
  102. configInput(CYCLE_A_INPUT, "Cycle A");
  103. configInput(CYCLE_B_INPUT, "Cycle B");
  104. configOutput(RISING_A_OUTPUT, "Rising A");
  105. configOutput(RISING_B_OUTPUT, "Rising B");
  106. configOutput(FALLING_A_OUTPUT, "Falling A");
  107. configOutput(FALLING_B_OUTPUT, "Falling B");
  108. configOutput(EOC_A_OUTPUT, "End of cycle A");
  109. configOutput(EOC_B_OUTPUT, "End of cycle B");
  110. configOutput(OUT_A_OUTPUT, "A");
  111. configOutput(OUT_B_OUTPUT, "B");
  112. configOutput(COMPARATOR_OUTPUT, "B > A");
  113. configOutput(MIN_OUTPUT, "Minimum of A and B");
  114. configOutput(MAX_OUTPUT, "Maximum of A and B");
  115. }
  116. void process(const ProcessArgs& args) override {
  117. int channels_in[2] = {};
  118. int channels_trig[2] = {};
  119. int channels[2] = {}; // the larger of in or trig (per-part)
  120. // determine number of channels:
  121. for (int part = 0; part < 2; part++) {
  122. channels_in[part] = inputs[IN_A_INPUT + part].getChannels();
  123. channels_trig[part] = inputs[TRIGG_A_INPUT + part].getChannels();
  124. channels[part] = std::max(channels_in[part], channels_trig[part]);
  125. channels[part] = std::max(1, channels[part]);
  126. outputs[OUT_A_OUTPUT + part].setChannels(channels[part]);
  127. outputs[RISING_A_OUTPUT + part].setChannels(channels[part]);
  128. outputs[FALLING_A_OUTPUT + part].setChannels(channels[part]);
  129. outputs[EOC_A_OUTPUT + part].setChannels(channels[part]);
  130. }
  131. // total number of active polyphony engines, accounting for both halves
  132. // (channels[0] / channels[1] are the number of active engines per section)
  133. const int channels_max = std::max(channels[0], channels[1]);
  134. outputs[COMPARATOR_OUTPUT].setChannels(channels_max);
  135. outputs[MIN_OUTPUT].setChannels(channels_max);
  136. outputs[MAX_OUTPUT].setChannels(channels_max);
  137. // loop over two parts of Rampage:
  138. for (int part = 0; part < 2; part++) {
  139. float_4 in[4] = {};
  140. float_4 in_trig[4] = {};
  141. float_4 riseCV[4] = {};
  142. float_4 fallCV[4] = {};
  143. float_4 cycle[4] = {};
  144. // get parameters:
  145. float minTime;
  146. switch ((int) params[RANGE_A_PARAM + part].getValue()) {
  147. case 0:
  148. minTime = 1e-2;
  149. break;
  150. case 1:
  151. minTime = 1e-3;
  152. break;
  153. default:
  154. minTime = 1e-1;
  155. break;
  156. }
  157. float_4 param_rise = params[RISE_A_PARAM + part].getValue() * 10.0f;
  158. float_4 param_fall = params[FALL_A_PARAM + part].getValue() * 10.0f;
  159. float_4 param_trig = params[TRIGG_A_PARAM + part].getValue() * 20.0f;
  160. float_4 param_cycle = params[CYCLE_A_PARAM + part].getValue() * 10.0f;
  161. for (int c = 0; c < channels[part]; c += 4) {
  162. riseCV[c / 4] = param_rise;
  163. fallCV[c / 4] = param_fall;
  164. cycle[c / 4] = param_cycle;
  165. in_trig[c / 4] = param_trig;
  166. }
  167. // read inputs:
  168. if (inputs[IN_A_INPUT + part].isConnected()) {
  169. for (int c = 0; c < channels[part]; c += 4)
  170. in[c / 4] = inputs[IN_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  171. }
  172. if (inputs[TRIGG_A_INPUT + part].isConnected()) {
  173. for (int c = 0; c < channels[part]; c += 4)
  174. in_trig[c / 4] += inputs[TRIGG_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  175. }
  176. if (inputs[EXP_CV_A_INPUT + part].isConnected()) {
  177. float_4 expCV[4] = {};
  178. for (int c = 0; c < channels[part]; c += 4)
  179. expCV[c / 4] = inputs[EXP_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  180. for (int c = 0; c < channels[part]; c += 4) {
  181. riseCV[c / 4] -= expCV[c / 4];
  182. fallCV[c / 4] -= expCV[c / 4];
  183. }
  184. }
  185. for (int c = 0; c < channels[part]; c += 4)
  186. riseCV[c / 4] += inputs[RISE_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  187. for (int c = 0; c < channels[part]; c += 4)
  188. fallCV[c / 4] += inputs[FALL_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  189. for (int c = 0; c < channels[part]; c += 4)
  190. cycle[c / 4] += inputs[CYCLE_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
  191. // start processing:
  192. for (int c = 0; c < channels[part]; c += 4) {
  193. // process SchmittTriggers
  194. float_4 trig_mask = trigger_4[part][c / 4].process(in_trig[c / 4] / 2.0, 0.1, 2.0);
  195. gate[part][c / 4] = ifelse(trig_mask, float_4::mask(), gate[part][c / 4]);
  196. in[c / 4] = ifelse(gate[part][c / 4], 10.0f, in[c / 4]);
  197. float_4 delta = in[c / 4] - out[part][c / 4];
  198. // rise / fall branching
  199. float_4 delta_gt_0 = delta > 0.f;
  200. float_4 delta_lt_0 = delta < 0.f;
  201. float_4 delta_eq_0 = ~(delta_lt_0 | delta_gt_0);
  202. float_4 rateCV = ifelse(delta_gt_0, riseCV[c / 4], 0.f);
  203. rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV);
  204. rateCV = clamp(rateCV, 0.f, 10.0f);
  205. float_4 rate = minTime * simd::pow(2.0f, rateCV);
  206. float shape = params[SHAPE_A_PARAM + part].getValue();
  207. out[part][c / 4] += shapeDelta(delta, rate, shape) * args.sampleTime;
  208. float_4 rising = simd::ifelse(delta_gt_0, (in[c / 4] - out[part][c / 4]) > 1e-3f, float_4::zero());
  209. float_4 falling = simd::ifelse(delta_lt_0, (in[c / 4] - out[part][c / 4]) < -1e-3f, float_4::zero());
  210. float_4 end_of_cycle = simd::andnot(falling, delta_lt_0);
  211. endOfCyclePulse[part][c / 4].trigger(end_of_cycle, 1e-3);
  212. gate[part][c / 4] = ifelse(simd::andnot(rising, delta_gt_0), 0.f, gate[part][c / 4]);
  213. gate[part][c / 4] = ifelse(end_of_cycle & (cycle[c / 4] >= 4.0f), float_4::mask(), gate[part][c / 4]);
  214. gate[part][c / 4] = ifelse(delta_eq_0, 0.f, gate[part][c / 4]);
  215. out[part][c / 4] = ifelse(rising | falling, out[part][c / 4], in[c / 4]);
  216. float_4 out_rising = ifelse(rising, 10.0f, 0.f);
  217. float_4 out_falling = ifelse(falling, 10.0f, 0.f);
  218. float_4 pulse = endOfCyclePulse[part][c / 4].process(args.sampleTime);
  219. float_4 out_EOC = ifelse(pulse, 10.f, 0.f);
  220. outputs[OUT_A_OUTPUT + part].setVoltageSimd(out[part][c / 4], c);
  221. outputs[RISING_A_OUTPUT + part].setVoltageSimd(out_rising, c);
  222. outputs[FALLING_A_OUTPUT + part].setVoltageSimd(out_falling, c);
  223. outputs[EOC_A_OUTPUT + part].setVoltageSimd(out_EOC, c);
  224. } // for(int c, ...)
  225. if (channels[part] == 1) {
  226. lights[RISING_A_LIGHT + 3 * part ].setSmoothBrightness(outputs[RISING_A_OUTPUT + part].getVoltage() / 10.f, args.sampleTime);
  227. lights[RISING_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  228. lights[RISING_A_LIGHT + 3 * part + 2].setBrightness(0.0f);
  229. lights[FALLING_A_LIGHT + 3 * part ].setSmoothBrightness(outputs[FALLING_A_OUTPUT + part].getVoltage() / 10.f, args.sampleTime);
  230. lights[FALLING_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  231. lights[FALLING_A_LIGHT + 3 * part + 2].setBrightness(0.0f);
  232. lights[OUT_A_LIGHT + 3 * part ].setSmoothBrightness(out[part][0].s[0] / 10.0, args.sampleTime);
  233. lights[OUT_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  234. lights[OUT_A_LIGHT + 3 * part + 2].setBrightness(0.0f);
  235. }
  236. else {
  237. lights[RISING_A_LIGHT + 3 * part ].setBrightness(0.0f);
  238. lights[RISING_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  239. lights[RISING_A_LIGHT + 3 * part + 2].setBrightness(10.0f);
  240. lights[FALLING_A_LIGHT + 3 * part ].setBrightness(0.0f);
  241. lights[FALLING_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  242. lights[FALLING_A_LIGHT + 3 * part + 2].setBrightness(10.0f);
  243. lights[OUT_A_LIGHT + 3 * part ].setBrightness(0.0f);
  244. lights[OUT_A_LIGHT + 3 * part + 1].setBrightness(0.0f);
  245. lights[OUT_A_LIGHT + 3 * part + 2].setBrightness(10.0f);
  246. }
  247. } // for (int part, ... )
  248. // Logic
  249. float balance = params[BALANCE_PARAM].getValue();
  250. for (int c = 0; c < channels_max; c += 4) {
  251. float_4 a = out[0][c / 4];
  252. float_4 b = out[1][c / 4];
  253. if (balance < 0.5)
  254. b *= 2.0f * balance;
  255. else if (balance > 0.5)
  256. a *= 2.0f * (1.0 - balance);
  257. float_4 comp = ifelse(b > a, 10.0f, 0.f);
  258. float_4 out_min = simd::fmin(a, b);
  259. float_4 out_max = simd::fmax(a, b);
  260. outputs[COMPARATOR_OUTPUT].setVoltageSimd(comp, c);
  261. outputs[MIN_OUTPUT].setVoltageSimd(out_min, c);
  262. outputs[MAX_OUTPUT].setVoltageSimd(out_max, c);
  263. }
  264. // Lights
  265. if (channels_max == 1) {
  266. lights[COMPARATOR_LIGHT ].setSmoothBrightness(outputs[COMPARATOR_OUTPUT].getVoltage(), args.sampleTime);
  267. lights[COMPARATOR_LIGHT + 1].setBrightness(0.0f);
  268. lights[COMPARATOR_LIGHT + 2].setBrightness(0.0f);
  269. lights[MIN_LIGHT ].setSmoothBrightness(outputs[MIN_OUTPUT].getVoltage(), args.sampleTime);
  270. lights[MIN_LIGHT + 1].setBrightness(0.0f);
  271. lights[MIN_LIGHT + 2].setBrightness(0.0f);
  272. lights[MAX_LIGHT ].setSmoothBrightness(outputs[MAX_OUTPUT].getVoltage(), args.sampleTime);
  273. lights[MAX_LIGHT + 1].setBrightness(0.0f);
  274. lights[MAX_LIGHT + 2].setBrightness(0.0f);
  275. }
  276. else {
  277. lights[COMPARATOR_LIGHT ].setBrightness(0.0f);
  278. lights[COMPARATOR_LIGHT + 1].setBrightness(0.0f);
  279. lights[COMPARATOR_LIGHT + 2].setBrightness(10.0f);
  280. lights[MIN_LIGHT ].setBrightness(0.0f);
  281. lights[MIN_LIGHT + 1].setBrightness(0.0f);
  282. lights[MIN_LIGHT + 2].setBrightness(10.0f);
  283. lights[MAX_LIGHT ].setBrightness(0.0f);
  284. lights[MAX_LIGHT + 1].setBrightness(0.0f);
  285. lights[MAX_LIGHT + 2].setBrightness(10.0f);
  286. }
  287. }
  288. };
  289. struct RampageWidget : ModuleWidget {
  290. RampageWidget(Rampage* module) {
  291. setModule(module);
  292. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/panels/Rampage.svg")));
  293. addChild(createWidget<Knurlie>(Vec(15, 0)));
  294. addChild(createWidget<Knurlie>(Vec(box.size.x - 30, 0)));
  295. addChild(createWidget<Knurlie>(Vec(15, 365)));
  296. addChild(createWidget<Knurlie>(Vec(box.size.x - 30, 365)));
  297. addParam(createParam<BefacoSwitch>(Vec(94, 32), module, Rampage::RANGE_A_PARAM));
  298. addParam(createParam<BefacoTinyKnob>(Vec(27, 90), module, Rampage::SHAPE_A_PARAM));
  299. addParam(createParam<BefacoPush>(Vec(72, 82), module, Rampage::TRIGG_A_PARAM));
  300. addParam(createParam<BefacoSlidePot>(Vec(16, 135), module, Rampage::RISE_A_PARAM));
  301. addParam(createParam<BefacoSlidePot>(Vec(57, 135), module, Rampage::FALL_A_PARAM));
  302. addParam(createParam<BefacoSwitch>(Vec(101, 238), module, Rampage::CYCLE_A_PARAM));
  303. addParam(createParam<BefacoSwitch>(Vec(147, 32), module, Rampage::RANGE_B_PARAM));
  304. addParam(createParam<BefacoTinyKnob>(Vec(217, 90), module, Rampage::SHAPE_B_PARAM));
  305. addParam(createParam<BefacoPush>(Vec(170, 82), module, Rampage::TRIGG_B_PARAM));
  306. addParam(createParam<BefacoSlidePot>(Vec(197, 135), module, Rampage::RISE_B_PARAM));
  307. addParam(createParam<BefacoSlidePot>(Vec(238, 135), module, Rampage::FALL_B_PARAM));
  308. addParam(createParam<BefacoSwitch>(Vec(141, 238), module, Rampage::CYCLE_B_PARAM));
  309. addParam(createParam<Davies1900hWhiteKnob>(Vec(117, 76), module, Rampage::BALANCE_PARAM));
  310. addInput(createInput<BefacoInputPort>(Vec(14, 30), module, Rampage::IN_A_INPUT));
  311. addInput(createInput<BefacoInputPort>(Vec(52, 37), module, Rampage::TRIGG_A_INPUT));
  312. addInput(createInput<BefacoInputPort>(Vec(8, 268), module, Rampage::RISE_CV_A_INPUT));
  313. addInput(createInput<BefacoInputPort>(Vec(67, 268), module, Rampage::FALL_CV_A_INPUT));
  314. addInput(createInput<BefacoInputPort>(Vec(38, 297), module, Rampage::EXP_CV_A_INPUT));
  315. addInput(createInput<BefacoInputPort>(Vec(102, 290), module, Rampage::CYCLE_A_INPUT));
  316. addInput(createInput<BefacoInputPort>(Vec(229, 30), module, Rampage::IN_B_INPUT));
  317. addInput(createInput<BefacoInputPort>(Vec(192, 37), module, Rampage::TRIGG_B_INPUT));
  318. addInput(createInput<BefacoInputPort>(Vec(176, 268), module, Rampage::RISE_CV_B_INPUT));
  319. addInput(createInput<BefacoInputPort>(Vec(237, 268), module, Rampage::FALL_CV_B_INPUT));
  320. addInput(createInput<BefacoInputPort>(Vec(207, 297), module, Rampage::EXP_CV_B_INPUT));
  321. addInput(createInput<BefacoInputPort>(Vec(143, 290), module, Rampage::CYCLE_B_INPUT));
  322. addOutput(createOutput<BefacoOutputPort>(Vec(8, 326), module, Rampage::RISING_A_OUTPUT));
  323. addOutput(createOutput<BefacoOutputPort>(Vec(68, 326), module, Rampage::FALLING_A_OUTPUT));
  324. addOutput(createOutput<BefacoOutputPort>(Vec(104, 326), module, Rampage::EOC_A_OUTPUT));
  325. addOutput(createOutput<BefacoOutputPort>(Vec(102, 195), module, Rampage::OUT_A_OUTPUT));
  326. addOutput(createOutput<BefacoOutputPort>(Vec(177, 326), module, Rampage::RISING_B_OUTPUT));
  327. addOutput(createOutput<BefacoOutputPort>(Vec(237, 326), module, Rampage::FALLING_B_OUTPUT));
  328. addOutput(createOutput<BefacoOutputPort>(Vec(140, 326), module, Rampage::EOC_B_OUTPUT));
  329. addOutput(createOutput<BefacoOutputPort>(Vec(142, 195), module, Rampage::OUT_B_OUTPUT));
  330. addOutput(createOutput<BefacoOutputPort>(Vec(122, 133), module, Rampage::COMPARATOR_OUTPUT));
  331. addOutput(createOutput<BefacoOutputPort>(Vec(89, 157), module, Rampage::MIN_OUTPUT));
  332. addOutput(createOutput<BefacoOutputPort>(Vec(155, 157), module, Rampage::MAX_OUTPUT));
  333. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT));
  334. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT));
  335. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(141, 174), module, Rampage::MAX_LIGHT));
  336. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(126, 185), module, Rampage::OUT_A_LIGHT));
  337. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(138, 185), module, Rampage::OUT_B_LIGHT));
  338. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(18, 312), module, Rampage::RISING_A_LIGHT));
  339. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT));
  340. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(187, 312), module, Rampage::RISING_B_LIGHT));
  341. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT));
  342. }
  343. };
  344. Model* modelRampage = createModel<Rampage, RampageWidget>("Rampage");