The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

334 lines
9.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: Box2DDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Showcases 2D graphics features.
  24. dependencies: juce_box2d, juce_core, juce_data_structures, juce_events,
  25. juce_graphics, juce_gui_basics
  26. exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: Box2DDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. //==============================================================================
  36. // (These classes and random functions are used inside the 3rd-party Box2D demo code)
  37. inline float32 RandomFloat() { return Random::getSystemRandom().nextFloat() * 2.0f - 1.0f; }
  38. inline float32 RandomFloat (float32 lo, float32 hi) { return Random::getSystemRandom().nextFloat() * (hi - lo) + lo; }
  39. struct Settings
  40. {
  41. b2Vec2 viewCenter { 0.0f, 20.0f };
  42. float32 hz = 60.0f;
  43. int velocityIterations = 8;
  44. int positionIterations = 3;
  45. int drawShapes = 1;
  46. int drawJoints = 1;
  47. int drawAABBs = 0;
  48. int drawPairs = 0;
  49. int drawContactPoints = 0;
  50. int drawContactNormals = 0;
  51. int drawContactForces = 0;
  52. int drawFrictionForces = 0;
  53. int drawCOMs = 0;
  54. int drawStats = 0;
  55. int drawProfile = 0;
  56. int enableWarmStarting = 1;
  57. int enableContinuous = 1;
  58. int enableSubStepping = 0;
  59. int pause = 0;
  60. int singleStep = 0;
  61. };
  62. struct Test
  63. {
  64. Test() {}
  65. virtual ~Test() {}
  66. virtual void Keyboard (unsigned char /*key*/) {}
  67. virtual void KeyboardUp (unsigned char /*key*/) {}
  68. std::unique_ptr<b2World> m_world { new b2World (b2Vec2 (0.0f, -10.0f)) };
  69. };
  70. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wimplicit-int-float-conversion")
  71. #include "../Assets/Box2DTests/AddPair.h"
  72. #include "../Assets/Box2DTests/ApplyForce.h"
  73. #include "../Assets/Box2DTests/Dominos.h"
  74. #include "../Assets/Box2DTests/Chain.h"
  75. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  76. //==============================================================================
  77. /** This list box just displays a StringArray and broadcasts a change message when the
  78. selected row changes.
  79. */
  80. class Box2DTestList final : public ListBoxModel,
  81. public ChangeBroadcaster
  82. {
  83. public:
  84. Box2DTestList (const StringArray& testList)
  85. : tests (testList)
  86. {}
  87. int getNumRows() override { return tests.size(); }
  88. void selectedRowsChanged (int /*lastRowSelected*/) override { sendChangeMessage(); }
  89. void paintListBoxItem (int row, Graphics& g, int w, int h, bool rowIsSelected) override
  90. {
  91. auto& lf = LookAndFeel::getDefaultLookAndFeel();
  92. if (rowIsSelected)
  93. g.fillAll (Colour::contrasting (lf.findColour (ListBox::textColourId),
  94. lf.findColour (ListBox::backgroundColourId)));
  95. g.setColour (lf.findColour (ListBox::textColourId));
  96. g.setFont ((float) h * 0.7f);
  97. g.drawText (tests[row], Rectangle<int> (0, 0, w, h).reduced (2),
  98. Justification::centredLeft, true);
  99. }
  100. private:
  101. StringArray tests;
  102. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DTestList)
  103. };
  104. //==============================================================================
  105. struct Box2DRenderComponent final : public Component
  106. {
  107. Box2DRenderComponent()
  108. {
  109. setOpaque (true);
  110. }
  111. void paint (Graphics& g) override
  112. {
  113. g.fillAll (Colours::white);
  114. if (currentTest.get() != nullptr)
  115. {
  116. Box2DRenderer renderer;
  117. renderer.render (g, *currentTest->m_world,
  118. -16.0f, 30.0f, 16.0f, -1.0f,
  119. getLocalBounds().toFloat().reduced (8.0f));
  120. }
  121. }
  122. std::unique_ptr<Test> currentTest;
  123. };
  124. //==============================================================================
  125. class Box2DDemo final : public Component,
  126. private Timer,
  127. private ChangeListener
  128. {
  129. public:
  130. enum Demos
  131. {
  132. addPair = 0,
  133. applyForce,
  134. dominoes,
  135. chain,
  136. numTests
  137. };
  138. Box2DDemo()
  139. : testsList (getTestsList())
  140. {
  141. setOpaque (true);
  142. setWantsKeyboardFocus (true);
  143. testsListModel.addChangeListener (this);
  144. addAndMakeVisible (renderComponent);
  145. addAndMakeVisible (testsListBox);
  146. testsListBox.setModel (&testsListModel);
  147. testsListBox.selectRow (dominoes);
  148. addAndMakeVisible (instructions);
  149. instructions.setMultiLine (true);
  150. instructions.setReadOnly (true);
  151. startTimerHz (60);
  152. setSize (500, 500);
  153. }
  154. ~Box2DDemo() override
  155. {
  156. testsListModel.removeChangeListener (this);
  157. }
  158. void paint (Graphics& g) override
  159. {
  160. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  161. }
  162. void resized() override
  163. {
  164. auto r = getLocalBounds().reduced (4);
  165. auto area = r.removeFromBottom (150);
  166. testsListBox.setBounds (area.removeFromLeft (150));
  167. area.removeFromLeft (4);
  168. instructions.setBounds (area);
  169. r.removeFromBottom (6);
  170. renderComponent.setBounds (r);
  171. }
  172. bool keyPressed (const KeyPress& key) override
  173. {
  174. if (renderComponent.currentTest.get() != nullptr)
  175. {
  176. // We override this to avoid the system beeping for an unused keypress
  177. switch (key.getTextCharacter())
  178. {
  179. case 'a':
  180. case 'w':
  181. case 'd':
  182. return true;
  183. default:
  184. break;
  185. }
  186. }
  187. return false;
  188. }
  189. private:
  190. StringArray testsList;
  191. Box2DTestList testsListModel { testsList };
  192. Box2DRenderComponent renderComponent;
  193. ListBox testsListBox;
  194. TextEditor instructions;
  195. static Test* createTest (int index)
  196. {
  197. switch (index)
  198. {
  199. case addPair: return new AddPair();
  200. case applyForce: return new ApplyForce();
  201. case dominoes: return new Dominos();
  202. case chain: return new Chain();
  203. default: break;
  204. }
  205. return nullptr;
  206. }
  207. static String getInstructions (int index)
  208. {
  209. switch (index)
  210. {
  211. case applyForce:
  212. return String ("Keys:") + newLine + "Left: \'a\'" + newLine
  213. + "Right: \'d\'" + newLine + "Forward: \'w\'";
  214. default:
  215. break;
  216. }
  217. return {};
  218. }
  219. void checkKeys()
  220. {
  221. if (renderComponent.currentTest.get() == nullptr)
  222. return;
  223. checkKeyCode ('a');
  224. checkKeyCode ('w');
  225. checkKeyCode ('d');
  226. }
  227. void checkKeyCode (const int keyCode)
  228. {
  229. if (KeyPress::isKeyCurrentlyDown (keyCode))
  230. renderComponent.currentTest->Keyboard ((unsigned char) keyCode);
  231. }
  232. void timerCallback() override
  233. {
  234. if (renderComponent.currentTest.get() == nullptr)
  235. return;
  236. if (isShowing())
  237. grabKeyboardFocus();
  238. checkKeys();
  239. renderComponent.currentTest->m_world->Step (1.0f / 60.0f, 6, 2);
  240. repaint();
  241. }
  242. void changeListenerCallback (ChangeBroadcaster* source) override
  243. {
  244. if (source == &testsListModel)
  245. {
  246. auto index = testsListBox.getSelectedRow();
  247. renderComponent.currentTest.reset (createTest (index));
  248. instructions.setText (getInstructions (index));
  249. repaint();
  250. }
  251. }
  252. void lookAndFeelChanged() override
  253. {
  254. instructions.applyFontToAllText (instructions.getFont());
  255. }
  256. static StringArray getTestsList()
  257. {
  258. return { "Add Pair Stress Test", "Apply Force", "Dominoes", "Chain" };
  259. }
  260. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DDemo)
  261. };