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.

323 lines
8.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../JuceDemoHeader.h"
  20. // (These classes and random functions are used inside the 3rd-party Box2D demo code)
  21. inline float32 RandomFloat() { return Random::getSystemRandom().nextFloat() * 2.0f - 1.0f; }
  22. inline float32 RandomFloat (float32 lo, float32 hi) { return Random::getSystemRandom().nextFloat() * (hi - lo) + lo; }
  23. struct Settings
  24. {
  25. b2Vec2 viewCenter { 0.0f, 20.0f };
  26. float32 hz = 60.0f;
  27. int velocityIterations = 8;
  28. int positionIterations = 3;
  29. int drawShapes = 1;
  30. int drawJoints = 1;
  31. int drawAABBs = 0;
  32. int drawPairs = 0;
  33. int drawContactPoints = 0;
  34. int drawContactNormals = 0;
  35. int drawContactForces = 0;
  36. int drawFrictionForces = 0;
  37. int drawCOMs = 0;
  38. int drawStats = 0;
  39. int drawProfile = 0;
  40. int enableWarmStarting = 1;
  41. int enableContinuous = 1;
  42. int enableSubStepping = 0;
  43. int pause = 0;
  44. int singleStep = 0;
  45. };
  46. struct Test
  47. {
  48. Test() : m_world (new b2World (b2Vec2 (0.0f, -10.0f))) {}
  49. virtual ~Test() {}
  50. virtual void Keyboard (unsigned char /*key*/) {}
  51. virtual void KeyboardUp (unsigned char /*key*/) {}
  52. ScopedPointer<b2World> m_world;
  53. };
  54. #include "Box2DTests/AddPair.h"
  55. #include "Box2DTests/ApplyForce.h"
  56. #include "Box2DTests/Dominos.h"
  57. #include "Box2DTests/Chain.h"
  58. //==============================================================================
  59. /** This list box just displays a StringArray and broadcasts a change message when the
  60. selected row changes.
  61. */
  62. class Box2DTestList : public ListBoxModel,
  63. public ChangeBroadcaster
  64. {
  65. public:
  66. Box2DTestList (const StringArray& testList) : tests (testList)
  67. {
  68. }
  69. int getNumRows() override { return tests.size(); }
  70. void paintListBoxItem (int row, Graphics& g, int w, int h, bool rowIsSelected) override
  71. {
  72. auto& lf = LookAndFeel::getDefaultLookAndFeel();
  73. if (rowIsSelected)
  74. g.fillAll (Colour::contrasting (lf.findColour (ListBox::textColourId),
  75. lf.findColour (ListBox::backgroundColourId)));
  76. const Font f (h * 0.7f);
  77. g.setColour (lf.findColour (ListBox::textColourId));
  78. g.setFont (f);
  79. g.drawText (tests[row], Rectangle<int> (0, 0, w, h).reduced (2),
  80. Justification::centredLeft, true);
  81. }
  82. void selectedRowsChanged (int /*lastRowSelected*/) override
  83. {
  84. sendChangeMessage();
  85. }
  86. private:
  87. StringArray tests;
  88. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DTestList)
  89. };
  90. //==============================================================================
  91. struct Box2DRenderComponent : public Component
  92. {
  93. Box2DRenderComponent()
  94. {
  95. setOpaque (true);
  96. }
  97. void paint (Graphics& g) override
  98. {
  99. g.fillAll (Colours::white);
  100. if (currentTest != nullptr)
  101. {
  102. Box2DRenderer renderer;
  103. renderer.render (g, *currentTest->m_world,
  104. -16.0f, 30.0f, 16.0f, -1.0f,
  105. getLocalBounds().toFloat().reduced (8.0f));
  106. }
  107. }
  108. ScopedPointer<Test> currentTest;
  109. };
  110. //==============================================================================
  111. class Box2DDemo : public Component,
  112. private Timer,
  113. private ChangeListener
  114. {
  115. public:
  116. enum Demos
  117. {
  118. addPair = 0,
  119. applyForce,
  120. dominoes,
  121. chain,
  122. numTests
  123. };
  124. Box2DDemo()
  125. : testsList (getTestsList()),
  126. testsListModel (testsList)
  127. {
  128. setOpaque (true);
  129. setWantsKeyboardFocus (true);
  130. testsListModel.addChangeListener (this);
  131. addAndMakeVisible (renderComponent);
  132. addAndMakeVisible (testsListBox);
  133. testsListBox.setModel (&testsListModel);
  134. testsListBox.selectRow (dominoes);
  135. addAndMakeVisible (instructions);
  136. instructions.setMultiLine (true);
  137. instructions.setReadOnly (true);
  138. startTimerHz (60);
  139. }
  140. ~Box2DDemo()
  141. {
  142. testsListModel.removeChangeListener (this);
  143. }
  144. void paint (Graphics& g) override
  145. {
  146. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  147. }
  148. void resized() override
  149. {
  150. auto r = getLocalBounds().reduced (4);
  151. auto area = r.removeFromBottom (150);
  152. testsListBox.setBounds (area.removeFromLeft (150));
  153. area.removeFromLeft (4);
  154. instructions.setBounds (area);
  155. r.removeFromBottom (6);
  156. renderComponent.setBounds (r);
  157. }
  158. bool keyPressed (const KeyPress& key) override
  159. {
  160. if (renderComponent.currentTest != nullptr)
  161. {
  162. // We override this to avoid the system beeping for an unused keypress
  163. switch (key.getTextCharacter())
  164. {
  165. case 'a':
  166. case 'w':
  167. case 'd':
  168. return true;
  169. default:
  170. break;
  171. }
  172. }
  173. return false;
  174. }
  175. private:
  176. StringArray testsList;
  177. Box2DTestList testsListModel;
  178. Box2DRenderComponent renderComponent;
  179. ListBox testsListBox;
  180. TextEditor instructions;
  181. static Test* createTest (int index)
  182. {
  183. switch (index)
  184. {
  185. case addPair: return new AddPair();
  186. case applyForce: return new ApplyForce();
  187. case dominoes: return new Dominos();
  188. case chain: return new Chain();
  189. default: break;
  190. }
  191. return nullptr;
  192. }
  193. static String getInstructions (int index)
  194. {
  195. switch (index)
  196. {
  197. case applyForce:
  198. {
  199. String s;
  200. s << "Keys:" << newLine
  201. << newLine
  202. << "Left: \'a\'" << newLine
  203. << "Right: \'d\'" << newLine
  204. << "Forward: \'w\'";
  205. return s;
  206. }
  207. default:
  208. break;
  209. }
  210. return String();
  211. }
  212. void checkKeys()
  213. {
  214. if (renderComponent.currentTest == nullptr)
  215. return;
  216. checkKeyCode ('a');
  217. checkKeyCode ('w');
  218. checkKeyCode ('d');
  219. }
  220. void checkKeyCode (const int keyCode)
  221. {
  222. if (KeyPress::isKeyCurrentlyDown (keyCode))
  223. renderComponent.currentTest->Keyboard ((unsigned char) keyCode);
  224. }
  225. void timerCallback() override
  226. {
  227. if (renderComponent.currentTest == nullptr)
  228. return;
  229. grabKeyboardFocus();
  230. checkKeys();
  231. renderComponent.currentTest->m_world->Step (1.0f / 60.0f, 6, 2);
  232. repaint();
  233. }
  234. void changeListenerCallback (ChangeBroadcaster* source) override
  235. {
  236. if (source == &testsListModel)
  237. {
  238. const int index = testsListBox.getSelectedRow();
  239. renderComponent.currentTest = createTest (index);
  240. instructions.setText (getInstructions (index));
  241. repaint();
  242. }
  243. }
  244. static StringArray getTestsList()
  245. {
  246. const char* tests[] =
  247. {
  248. "Add Pair Stress Test",
  249. "Apply Force",
  250. "Dominoes",
  251. "Chain"
  252. };
  253. jassert (numElementsInArray (tests) == numTests);
  254. return StringArray (tests, numElementsInArray (tests));
  255. }
  256. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DDemo)
  257. };
  258. // This static object will register this demo type in a global list of demos..
  259. static JuceDemoType<Box2DDemo> demo ("29 Graphics: Box 2D");