Form.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. #include "Base.h"
  2. #include "Form.h"
  3. #include "AbsoluteLayout.h"
  4. #include "FlowLayout.h"
  5. #include "VerticalLayout.h"
  6. #include "Game.h"
  7. #include "Theme.h"
  8. #include "Label.h"
  9. #include "Button.h"
  10. #include "CheckBox.h"
  11. #include "Scene.h"
  12. // Default form shaders
  13. #define FORM_VSH "res/shaders/form.vert"
  14. #define FORM_FSH "res/shaders/form.frag"
  15. namespace gameplay
  16. {
  17. static Effect* __formEffect = NULL;
  18. static std::vector<Form*> __forms;
  19. Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL),
  20. _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0), _isGamepad(false)
  21. {
  22. }
  23. Form::~Form()
  24. {
  25. SAFE_DELETE(_spriteBatch);
  26. SAFE_RELEASE(_frameBuffer);
  27. SAFE_RELEASE(_theme);
  28. if (__formEffect)
  29. {
  30. if (__formEffect->getRefCount() == 1)
  31. {
  32. __formEffect->release();
  33. __formEffect = NULL;
  34. }
  35. }
  36. // Remove this Form from the global list.
  37. std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
  38. if (it != __forms.end())
  39. {
  40. __forms.erase(it);
  41. }
  42. }
  43. Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
  44. {
  45. GP_ASSERT(style);
  46. Layout* layout;
  47. switch (layoutType)
  48. {
  49. case Layout::LAYOUT_ABSOLUTE:
  50. layout = AbsoluteLayout::create();
  51. break;
  52. case Layout::LAYOUT_FLOW:
  53. layout = FlowLayout::create();
  54. break;
  55. case Layout::LAYOUT_VERTICAL:
  56. layout = VerticalLayout::create();
  57. break;
  58. default:
  59. GP_ERROR("Unsupported layout type '%d'.", layoutType);
  60. break;
  61. }
  62. Form* form = new Form();
  63. if (id)
  64. form->_id = id;
  65. form->_style = style;
  66. form->_layout = layout;
  67. form->_theme = style->getTheme();
  68. form->_theme->addRef();
  69. form->updateFrameBuffer();
  70. __forms.push_back(form);
  71. return form;
  72. }
  73. Form* Form::create(const char* url)
  74. {
  75. // Load Form from .form file.
  76. Properties* properties = Properties::create(url);
  77. if (properties == NULL)
  78. {
  79. GP_ASSERT(properties);
  80. return NULL;
  81. }
  82. // Check if the Properties is valid and has a valid namespace.
  83. Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
  84. assert(formProperties);
  85. if (!formProperties || !(strcmp(formProperties->getNamespace(), "form") == 0))
  86. {
  87. GP_ASSERT(formProperties);
  88. SAFE_DELETE(properties);
  89. return NULL;
  90. }
  91. // Create new form with given ID, theme and layout.
  92. std::string themeFile;
  93. formProperties->getPath("theme", &themeFile);
  94. // Parse layout
  95. Layout* layout = NULL;
  96. Properties* layoutNS = formProperties->getNamespace("layout", true, false);
  97. if (layoutNS)
  98. {
  99. Layout::Type layoutType = getLayoutType(layoutNS->getString("type"));
  100. switch (layoutType)
  101. {
  102. case Layout::LAYOUT_ABSOLUTE:
  103. layout = AbsoluteLayout::create();
  104. break;
  105. case Layout::LAYOUT_FLOW:
  106. layout = FlowLayout::create();
  107. static_cast<FlowLayout*>(layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
  108. break;
  109. case Layout::LAYOUT_VERTICAL:
  110. layout = VerticalLayout::create();
  111. static_cast<VerticalLayout*>(layout)->setSpacing(layoutNS->getInt("spacing"));
  112. break;
  113. }
  114. }
  115. else
  116. {
  117. switch (getLayoutType(formProperties->getString("layout")))
  118. {
  119. case Layout::LAYOUT_ABSOLUTE:
  120. layout = AbsoluteLayout::create();
  121. break;
  122. case Layout::LAYOUT_FLOW:
  123. layout = FlowLayout::create();
  124. break;
  125. case Layout::LAYOUT_VERTICAL:
  126. layout = VerticalLayout::create();
  127. break;
  128. }
  129. }
  130. if (layout == NULL)
  131. {
  132. GP_ERROR("Unsupported layout type for form: %s", url);
  133. }
  134. Theme* theme = Theme::create(themeFile.c_str());
  135. GP_ASSERT(theme);
  136. Form* form = new Form();
  137. form->_layout = layout;
  138. form->_theme = theme;
  139. Theme::Style* style = NULL;
  140. const char* styleName = formProperties->getString("style");
  141. if (styleName)
  142. {
  143. style = theme->getStyle(styleName);
  144. }
  145. else
  146. {
  147. style = theme->getEmptyStyle();
  148. }
  149. form->initialize(style, formProperties);
  150. form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", false);
  151. form->_scroll = getScroll(formProperties->getString("scroll"));
  152. form->_scrollBarsAutoHide = formProperties->getBool("scrollBarsAutoHide");
  153. if (form->_scrollBarsAutoHide)
  154. {
  155. form->_scrollBarOpacity = 0.0f;
  156. }
  157. // Add all the controls to the form.
  158. form->addControls(theme, formProperties);
  159. SAFE_DELETE(properties);
  160. form->updateFrameBuffer();
  161. __forms.push_back(form);
  162. return form;
  163. }
  164. Form* Form::getForm(const char* id)
  165. {
  166. std::vector<Form*>::const_iterator it;
  167. for (it = __forms.begin(); it < __forms.end(); ++it)
  168. {
  169. Form* f = *it;
  170. GP_ASSERT(f);
  171. if (strcmp(id, f->getId()) == 0)
  172. {
  173. return f;
  174. }
  175. }
  176. return NULL;
  177. }
  178. Theme* Form::getTheme() const
  179. {
  180. return _theme;
  181. }
  182. void Form::updateFrameBuffer()
  183. {
  184. float width = _absoluteClipBounds.width;
  185. float height = _absoluteClipBounds.height;
  186. SAFE_RELEASE(_frameBuffer);
  187. SAFE_DELETE(_spriteBatch);
  188. if (width != 0.0f && height != 0.0f)
  189. {
  190. // Width and height must be powers of two to create a texture.
  191. unsigned int w = nextPowerOfTwo(width);
  192. unsigned int h = nextPowerOfTwo(height);
  193. _u2 = width / (float)w;
  194. _v1 = height / (float)h;
  195. _frameBuffer = FrameBuffer::create(_id.c_str(), w, h);
  196. GP_ASSERT(_frameBuffer);
  197. // Re-create projection matrix for drawing onto framebuffer
  198. Matrix::createOrthographicOffCenter(0, width, height, 0, 0, 1, &_projectionMatrix);
  199. // Re-create sprite batch
  200. _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
  201. GP_ASSERT(_spriteBatch);
  202. // Compute full-viewport ortho matrix for drawing frame buffer onto screen
  203. Matrix viewportProjection;
  204. Matrix::createOrthographicOffCenter(0, Game::getInstance()->getViewport().width, Game::getInstance()->getViewport().height, 0, 0, 1, &viewportProjection);
  205. _spriteBatch->setProjectionMatrix(viewportProjection);
  206. // Clear the framebuffer black
  207. Game* game = Game::getInstance();
  208. FrameBuffer* previousFrameBuffer = _frameBuffer->bind();
  209. Rectangle previousViewport = game->getViewport();
  210. game->setViewport(Rectangle(0, 0, width, height));
  211. _theme->setProjectionMatrix(_projectionMatrix);
  212. game->clear(Game::CLEAR_COLOR, Vector4::zero(), 1.0, 0);
  213. previousFrameBuffer->bind();
  214. game->setViewport(previousViewport);
  215. // Force any attached node to be updated
  216. setNode(_node);
  217. }
  218. }
  219. static Effect* createEffect()
  220. {
  221. Effect* effect = NULL;
  222. if (__formEffect == NULL)
  223. {
  224. __formEffect = Effect::createFromFile(FORM_VSH, FORM_FSH);
  225. if (__formEffect == NULL)
  226. {
  227. GP_ERROR("Unable to load form effect.");
  228. return NULL;
  229. }
  230. effect = __formEffect;
  231. }
  232. else
  233. {
  234. effect = __formEffect;
  235. }
  236. return effect;
  237. }
  238. void Form::setNode(Node* node)
  239. {
  240. // If we were already attached to a node, remove ourself from it
  241. if (_node)
  242. {
  243. _node->setModel(NULL);
  244. _nodeQuad = NULL;
  245. _nodeMaterial = NULL;
  246. _node = NULL;
  247. }
  248. if (node)
  249. {
  250. // Set this Form up to be 3D by initializing a quad.
  251. float x2 = _absoluteBounds.width;
  252. float y2 = _absoluteBounds.height;
  253. float vertices[] =
  254. {
  255. 0, y2, 0, 0, _v1,
  256. 0, 0, 0, 0, 0,
  257. x2, y2, 0, _u2, _v1,
  258. x2, 0, 0, _u2, 0
  259. };
  260. VertexFormat::Element elements[] =
  261. {
  262. VertexFormat::Element(VertexFormat::POSITION, 3),
  263. VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
  264. };
  265. Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
  266. GP_ASSERT(mesh);
  267. mesh->setPrimitiveType(Mesh::TRIANGLE_STRIP);
  268. mesh->setVertexData(vertices, 0, 4);
  269. _nodeQuad = Model::create(mesh);
  270. SAFE_RELEASE(mesh);
  271. GP_ASSERT(_nodeQuad);
  272. // Create the effect and material
  273. Effect* effect = createEffect();
  274. GP_ASSERT(effect);
  275. _nodeMaterial = Material::create(effect);
  276. GP_ASSERT(_nodeMaterial);
  277. _nodeQuad->setMaterial(_nodeMaterial);
  278. _nodeMaterial->release();
  279. node->setModel(_nodeQuad);
  280. _nodeQuad->release();
  281. // Bind the WorldViewProjection matrix.
  282. _nodeMaterial->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
  283. // Bind the texture from the framebuffer and set the texture to clamp
  284. if (_frameBuffer)
  285. {
  286. Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
  287. GP_ASSERT(sampler);
  288. sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
  289. _nodeMaterial->getParameter("u_texture")->setValue(sampler);
  290. sampler->release();
  291. }
  292. RenderState::StateBlock* rsBlock = _nodeMaterial->getStateBlock();
  293. rsBlock->setDepthWrite(true);
  294. rsBlock->setBlend(true);
  295. rsBlock->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
  296. rsBlock->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
  297. }
  298. _node = node;
  299. }
  300. void Form::update(float elapsedTime)
  301. {
  302. if (true)//isDirty())
  303. {
  304. update(NULL, Vector2::zero());
  305. // Cache themed attributes for performance.
  306. _skin = getSkin(_state);
  307. _opacity = getOpacity(_state);
  308. GP_ASSERT(_layout);
  309. if (_scroll != SCROLL_NONE)
  310. {
  311. updateScroll();
  312. }
  313. else
  314. {
  315. _layout->update(this, Vector2::zero());
  316. }
  317. }
  318. }
  319. void Form::update(const Control* container, const Vector2& offset)
  320. {
  321. // Store previous absolute bounds
  322. Rectangle oldAbsoluteClipBounds = _absoluteClipBounds;
  323. _layout->align(this, NULL);
  324. Container::update(container, offset);
  325. if (_absoluteClipBounds.width != oldAbsoluteClipBounds.width || _absoluteClipBounds.height != oldAbsoluteClipBounds.height)
  326. {
  327. updateFrameBuffer();
  328. }
  329. }
  330. void Form::draw()
  331. {
  332. if (!_visible || !_frameBuffer)
  333. return;
  334. // The first time a form is drawn, its contents are rendered into a framebuffer.
  335. // The framebuffer will only be drawn into again when the contents of the form change.
  336. // If this form has a node then it's a 3D form and the framebuffer will be used
  337. // to texture a quad. The quad will be given the same dimensions as the form and
  338. // must be transformed appropriately by the user, unless they call setQuad() themselves.
  339. // On the other hand, if this form has not been set on a node, SpriteBatch will be used
  340. // to render the contents of the framebuffer directly to the display.
  341. // Check whether this form has changed since the last call to draw() and if so, render into the framebuffer.
  342. if (true)//isDirty())
  343. {
  344. FrameBuffer* previousFrameBuffer = _frameBuffer->bind();
  345. Game* game = Game::getInstance();
  346. Rectangle prevViewport = game->getViewport();
  347. game->setViewport(Rectangle(0, 0, _absoluteClipBounds.width, _absoluteClipBounds.height));
  348. GP_ASSERT(_theme);
  349. _theme->setProjectionMatrix(_projectionMatrix);
  350. // By setting needsClear to true here, an optimization meant to clear and redraw only areas of the form
  351. // that have changed is disabled. Currently, repositioning controls can result in areas of the screen being cleared
  352. // after another control has been drawn there. This should probably be done in two passes -- one to clear areas where
  353. // dirty controls were last frame, and another to draw them where they are now.
  354. Container::draw(_theme->getSpriteBatch(), _absoluteClipBounds, /*_skin != NULL*/ true, false, _absoluteClipBounds.height);
  355. // Restore the previous game viewport.
  356. game->setViewport(prevViewport);
  357. // Rebind the previous framebuffer and game viewport.
  358. previousFrameBuffer->bind();
  359. }
  360. // Draw either with a 3D quad or sprite batch.
  361. if (_node)
  362. {
  363. // If we have the node set, then draw a 3D quad model.
  364. _nodeQuad->draw();
  365. }
  366. else
  367. {
  368. // Otherwise we draw the framebuffer in ortho space with a spritebatch.
  369. _spriteBatch->start();
  370. _spriteBatch->draw(_bounds.x, _bounds.y, 0, _bounds.width, _bounds.height, 0, _v1, _u2, 0, Vector4::one());
  371. _spriteBatch->finish();
  372. }
  373. }
  374. const char* Form::getType() const
  375. {
  376. return "form";
  377. }
  378. void Form::updateInternal(float elapsedTime)
  379. {
  380. size_t size = __forms.size();
  381. for (size_t i = 0; i < size; ++i)
  382. {
  383. Form* form = __forms[i];
  384. GP_ASSERT(form);
  385. if (form->isEnabled() && form->isVisible())
  386. {
  387. form->update(elapsedTime);
  388. }
  389. }
  390. }
  391. static bool shouldPropagateTouchEvent(Control::State state, Touch::TouchEvent evt, const Rectangle& bounds, int x, int y)
  392. {
  393. return (state != Control::NORMAL ||
  394. (evt == Touch::TOUCH_PRESS &&
  395. x >= bounds.x &&
  396. x <= bounds.x + bounds.width &&
  397. y >= bounds.y &&
  398. y <= bounds.y + bounds.height));
  399. }
  400. bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  401. {
  402. // Check for a collision with each Form in __forms.
  403. // Pass the event on.
  404. size_t size = __forms.size();
  405. for (size_t i = 0; i < size; ++i)
  406. {
  407. Form* form = __forms[i];
  408. GP_ASSERT(form);
  409. if (form->isEnabled() && form->isVisible())
  410. {
  411. if (form->_node)
  412. {
  413. Vector3 point;
  414. if (form->projectPoint(x, y, &point))
  415. {
  416. const Rectangle& bounds = form->getBounds();
  417. if (shouldPropagateTouchEvent(form->getState(), evt, bounds, point.x, point.y))
  418. {
  419. if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
  420. return true;
  421. }
  422. }
  423. }
  424. else
  425. {
  426. // Simply compare with the form's bounds.
  427. const Rectangle& bounds = form->getBounds();
  428. if (shouldPropagateTouchEvent(form->getState(), evt, bounds, x, y))
  429. {
  430. // Pass on the event's position relative to the form.
  431. if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
  432. return true;
  433. }
  434. }
  435. }
  436. }
  437. return false;
  438. }
  439. bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
  440. {
  441. size_t size = __forms.size();
  442. for (size_t i = 0; i < size; ++i)
  443. {
  444. Form* form = __forms[i];
  445. GP_ASSERT(form);
  446. if (form->isEnabled() && form->isVisible() && form->hasFocus() && !form->_isGamepad)
  447. {
  448. if (form->keyEvent(evt, key))
  449. return true;
  450. }
  451. }
  452. return false;
  453. }
  454. static bool shouldPropagateMouseEvent(Control::State state, Mouse::MouseEvent evt, const Rectangle& bounds, int x, int y)
  455. {
  456. return (state != Control::NORMAL ||
  457. ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
  458. evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
  459. evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
  460. evt == Mouse::MOUSE_MOVE ||
  461. evt == Mouse::MOUSE_WHEEL) &&
  462. x >= bounds.x &&
  463. x <= bounds.x + bounds.width &&
  464. y >= bounds.y &&
  465. y <= bounds.y + bounds.height));
  466. }
  467. bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
  468. {
  469. // Do not process mouse input when mouse is captured
  470. if (Game::getInstance()->isMouseCaptured())
  471. return false;
  472. for (size_t i = 0; i < __forms.size(); ++i)
  473. {
  474. Form* form = __forms[i];
  475. GP_ASSERT(form);
  476. if (form->isEnabled() && form->isVisible())
  477. {
  478. if (form->_node)
  479. {
  480. Vector3 point;
  481. if (form->projectPoint(x, y, &point))
  482. {
  483. const Rectangle& bounds = form->getBounds();
  484. if (shouldPropagateMouseEvent(form->getState(), evt, bounds, point.x, point.y))
  485. {
  486. if (form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta))
  487. return true;
  488. }
  489. }
  490. }
  491. else
  492. {
  493. // Simply compare with the form's bounds.
  494. const Rectangle& bounds = form->getBounds();
  495. if (shouldPropagateMouseEvent(form->getState(), evt, bounds, x, y))
  496. {
  497. // Pass on the event's position relative to the form.
  498. if (form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta))
  499. return true;
  500. }
  501. }
  502. }
  503. }
  504. return false;
  505. }
  506. void Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
  507. {
  508. for (size_t i = 0; i < __forms.size(); ++i)
  509. {
  510. Form* form = __forms[i];
  511. GP_ASSERT(form);
  512. if (form->isEnabled() && form->isVisible() && form->hasFocus())
  513. {
  514. if (form->gamepadEvent(evt, gamepad, analogIndex))
  515. return;
  516. }
  517. }
  518. }
  519. void Form::resizeEventInternal(unsigned int width, unsigned int height)
  520. {
  521. for (size_t i = 0; i < __forms.size(); ++i)
  522. {
  523. Form* form = __forms[i];
  524. if (form)
  525. {
  526. if (form->_spriteBatch)
  527. {
  528. // Update viewport projection matrix
  529. Matrix viewportProjection;
  530. Matrix::createOrthographicOffCenter(0, Game::getInstance()->getViewport().width, Game::getInstance()->getViewport().height, 0, 0, 1, &viewportProjection);
  531. form->_spriteBatch->setProjectionMatrix(viewportProjection);
  532. }
  533. // Dirty the form
  534. form->_dirty = true;
  535. }
  536. }
  537. }
  538. bool Form::projectPoint(int x, int y, Vector3* point)
  539. {
  540. Scene* scene = _node->getScene();
  541. Camera* camera;
  542. if (scene && (camera = scene->getActiveCamera()))
  543. {
  544. // Get info about the form's position.
  545. Matrix m = _node->getWorldMatrix();
  546. Vector3 pointOnPlane(0, 0, 0);
  547. m.transformPoint(&pointOnPlane);
  548. // Unproject point into world space.
  549. Ray ray;
  550. camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
  551. // Find the quad's plane. We know its normal is the quad's forward vector.
  552. Vector3 normal = _node->getForwardVectorWorld().normalize();
  553. // To get the plane's distance from the origin, we project a point on the
  554. // plane onto the plane's normal vector.
  555. const float distance = fabs(Vector3::dot(pointOnPlane, normal));
  556. Plane plane(normal, -distance);
  557. // Check for collision with plane.
  558. float collisionDistance = ray.intersects(plane);
  559. if (collisionDistance != Ray::INTERSECTS_NONE)
  560. {
  561. // Multiply the ray's direction vector by collision distance and add that to the ray's origin.
  562. point->set(ray.getOrigin() + collisionDistance*ray.getDirection());
  563. // Project this point into the plane.
  564. m.invert();
  565. m.transformPoint(point);
  566. return true;
  567. }
  568. }
  569. return false;
  570. }
  571. unsigned int Form::nextPowerOfTwo(unsigned int v)
  572. {
  573. if (!((v & (v - 1)) == 0))
  574. {
  575. v--;
  576. v |= v >> 1;
  577. v |= v >> 2;
  578. v |= v >> 4;
  579. v |= v >> 8;
  580. v |= v >> 16;
  581. return v + 1;
  582. }
  583. else
  584. {
  585. return v;
  586. }
  587. }
  588. }