MyGUI  3.2.1
MyGUI_PolygonalSkin.cpp
Go to the documentation of this file.
1 /*
2  * This source file is part of MyGUI. For the latest info, see http://mygui.info/
3  * Distributed under the MIT License
4  * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT)
5  */
6 
7 #include "MyGUI_Precompiled.h"
8 #include "MyGUI_PolygonalSkin.h"
9 #include "MyGUI_RenderItem.h"
10 #include "MyGUI_CommonStateInfo.h"
11 #include "MyGUI_RenderManager.h"
12 #include "MyGUI_GeometryUtility.h"
13 
14 namespace MyGUI
15 {
16 
18  mGeometryOutdated(false),
19  mLineWidth(1.0f),
20  mLineStroke(0),
21  mLineLength(0.0f),
22  mVertexCount(VertexQuad::VertexCount),
23  mEmptyView(false),
24  mCurrentColour(0xFFFFFFFF),
25  mNode(nullptr),
26  mRenderItem(nullptr)
27  {
28  mVertexFormat = RenderManager::getInstance().getVertexFormat();
29  }
30 
32  {
33  }
34 
35  inline float len(float x, float y)
36  {
37  return sqrt(x * x + y * y);
38  }
39 
40  void PolygonalSkin::setPoints(const std::vector<FloatPoint>& _points)
41  {
42  if (_points.size() < 2)
43  {
44  mVertexCount = 0;
45  mResultVerticiesPos.clear();
46  mResultVerticiesUV.clear();
47  mLinePoints = _points;
48  return;
49  }
50 
51  VectorFloatPoint finalPoints;
52  finalPoints.reserve(_points.size());
53 
54  mLineLength = 0.0f;
55  FloatPoint point = _points[0];
56  finalPoints.push_back(point);
57  // ignore repeating points
58  for (std::vector<FloatPoint>::const_iterator iter = _points.begin() + 1; iter != _points.end(); ++iter)
59  {
60  if (point != *iter)
61  {
62  finalPoints.push_back(*iter);
63  mLineLength += len(iter->left - point.left, iter->top - point.top);
64  point = *iter;
65  }
66  }
67 
68  mLinePoints = finalPoints;
69 
70 #ifdef MYGUI_NO_POLYGONAL_SKIN_CROPPING
71  size_t count = (mLinePoints.size() - 1) * VertexQuad::VertexCount * 2;
72 #else
73  // it's too hard to calculate maximum possible verticies count and worst
74  // approximation gives 7 times more verticies than in not cropped geometry
75  // so we multiply count by 2, because this looks enough
76  size_t count = (mLinePoints.size() - 1) * VertexQuad::VertexCount * 2 * 2;
77 #endif
78  if (count > mVertexCount)
79  {
80  mVertexCount = count;
81  if (nullptr != mRenderItem) mRenderItem->reallockDrawItem(this, mVertexCount);
82  }
83 
84  _updateView();
85  }
86 
87  void PolygonalSkin::setWidth(float _width)
88  {
89  mLineWidth = _width;
90  _updateView();
91  }
92 
93  void PolygonalSkin::setStroke(size_t _value)
94  {
95  mLineStroke = _value;
96  _updateView();
97  }
98 
99  void PolygonalSkin::setVisible(bool _visible)
100  {
101  if (mVisible == _visible)
102  return;
103 
104  mVisible = _visible;
105  mGeometryOutdated = true;
106 
107  if (nullptr != mNode)
108  mNode->outOfDate(mRenderItem);
109  }
110 
111  void PolygonalSkin::setAlpha(float _alpha)
112  {
113  uint32 alpha = ((uint8)(_alpha * 255) << 24);
114  mCurrentColour = (mCurrentColour & 0x00FFFFFF) | (alpha & 0xFF000000);
115 
116  if (nullptr != mNode)
117  mNode->outOfDate(mRenderItem);
118  }
119 
121  {
122  mGeometryOutdated = true;
123 
124  if (nullptr != mNode)
125  mNode->outOfDate(mRenderItem);
126  }
127 
128  void PolygonalSkin::_setAlign(const IntSize& _oldsize)
129  {
130  // необходимо разобраться
131  bool need_update = true;
132 
133  // первоначальное выравнивание
134  if (mAlign.isHStretch())
135  {
136  // растягиваем
137  mCoord.width = mCoord.width + (mCroppedParent->getWidth() - _oldsize.width);
138  need_update = true;
139  mIsMargin = true; // при изменении размеров все пересчитывать
140  }
141  else if (mAlign.isRight())
142  {
143  // двигаем по правому краю
144  mCoord.left = mCoord.left + (mCroppedParent->getWidth() - _oldsize.width);
145  need_update = true;
146  }
147  else if (mAlign.isHCenter())
148  {
149  // выравнивание по горизонтали без растяжения
151  need_update = true;
152  }
153 
154  if (mAlign.isVStretch())
155  {
156  // растягиваем
158  need_update = true;
159  mIsMargin = true; // при изменении размеров все пересчитывать
160  }
161  else if (mAlign.isBottom())
162  {
163  // двигаем по нижнему краю
164  mCoord.top = mCoord.top + (mCroppedParent->getHeight() - _oldsize.height);
165  need_update = true;
166  }
167  else if (mAlign.isVCenter())
168  {
169  // выравнивание по вертикали без растяжения
171  need_update = true;
172  }
173 
174  if (need_update)
175  {
176  mCurrentCoord = mCoord;
177  _updateView();
178  }
179  }
180 
182  {
183  bool margin = _checkMargin();
184 
185  mEmptyView = ((0 >= _getViewWidth()) || (0 >= _getViewHeight()));
186 
187  mGeometryOutdated = true;
188 
189  mCurrentCoord.left = mCoord.left + mMargin.left;
190  mCurrentCoord.top = mCoord.top + mMargin.top;
191 
192  // вьюпорт стал битым
193  if (margin)
194  {
195  // проверка на полный выход за границу
196  if (_checkOutside())
197  {
198  // запоминаем текущее состояние
199  mIsMargin = margin;
200 
201  // обновить перед выходом
202  if (nullptr != mNode)
203  mNode->outOfDate(mRenderItem);
204  return;
205  }
206  }
207 
208  // мы обрезаны или были обрезаны
209  if (mIsMargin || margin)
210  {
211  mCurrentCoord.width = _getViewWidth();
212  mCurrentCoord.height = _getViewHeight();
213  }
214 
215  // запоминаем текущее состояние
216  mIsMargin = margin;
217 
218  if (nullptr != mNode)
219  mNode->outOfDate(mRenderItem);
220  }
221 
223  {
224  MYGUI_ASSERT(!mRenderItem, "mRenderItem must be nullptr");
225 
226  mNode = _node;
227  mRenderItem = mNode->addToRenderItem(_texture, true, false);
228  mRenderItem->addDrawItem(this, mVertexCount);
229  }
230 
232  {
233  MYGUI_ASSERT(mRenderItem, "mRenderItem must be not nullptr");
234 
235  mNode = nullptr;
236  mRenderItem->removeDrawItem(this);
237  mRenderItem = nullptr;
238  }
239 
241  {
242  if (!mVisible || mEmptyView)
243  return;
244 
245  bool update = mRenderItem->getCurrentUpdate();
246  if (update)
247  mGeometryOutdated = true;
248 
249  Vertex* verticies = mRenderItem->getCurrentVertexBuffer();
250 
251  float vertex_z = mNode->getNodeDepth();
252 
253  if (mGeometryOutdated)
254  {
256  }
257 
258  size_t size = mResultVerticiesPos.size();
259 
260  for (size_t i = 0; i < size; ++i)
261  {
262  verticies[i].set(mResultVerticiesPos[i].left, mResultVerticiesPos[i].top, vertex_z, mResultVerticiesUV[i].left, mResultVerticiesUV[i].top, mCurrentColour);
263  }
264 
265  mRenderItem->setLastVertexCount(size);
266  }
267 
268  void PolygonalSkin::_setColour(const Colour& _value)
269  {
270  uint32 colour = texture_utility::toColourARGB(_value);
271  texture_utility::convertColour(colour, mVertexFormat);
272  mCurrentColour = (colour & 0x00FFFFFF) | (mCurrentColour & 0xFF000000);
273 
274  if (nullptr != mNode)
275  mNode->outOfDate(mRenderItem);
276  }
277 
279  {
281  }
282 
284  {
285  mCurrentTexture = _rect;
286 
287  mGeometryOutdated = true;
288 
289  if (nullptr != mNode)
290  mNode->outOfDate(mRenderItem);
291  }
292 
294  {
295  if (mLinePoints.size() < 2)
296  return;
297  if (!mRenderItem || !mRenderItem->getRenderTarget())
298  return;
299 
300  mGeometryOutdated = false;
301 
302  // using mCurrentCoord as rectangle where we draw polygons
303 
304  // base texture coordinates
305  FloatPoint baseVerticiesUV[4] =
306  {
307  FloatPoint(mCurrentTexture.left, mCurrentTexture.top),
308  FloatPoint(mCurrentTexture.right, mCurrentTexture.top),
309  FloatPoint(mCurrentTexture.right, mCurrentTexture.bottom),
310  FloatPoint(mCurrentTexture.left, mCurrentTexture.bottom)
311  };
312 
313  // UV vectors
314  FloatPoint vectorU = baseVerticiesUV[1] - baseVerticiesUV[0];
315  FloatPoint vectorV = baseVerticiesUV[3] - baseVerticiesUV[0];
316 
317  FloatPoint vertex1;
318  FloatPoint vertex2;
319  mResultVerticiesPos.clear();
320  mResultVerticiesUV.clear();
321  // add first two verticies
322  FloatPoint normal = _getPerpendicular(mLinePoints[0], mLinePoints[1]);
323 
324  FloatPoint points[2] = {mLinePoints[0] + normal, mLinePoints[0] - normal};
325  FloatPoint pointsUV[2] = {baseVerticiesUV[0], baseVerticiesUV[3]};
326 
327  bool draw = true;
328  size_t stroke = 0;
329 
330  // add other verticies
331  float currentLength = 0.0f;
332  for (size_t i = 1; i < mLinePoints.size(); ++i)
333  {
334  if (mLineStroke != 0)
335  {
336  stroke++;
337  if (stroke == mLineStroke)
338  {
339  stroke = 0;
340  draw = !draw;
341  }
342  }
343 
344  currentLength += len(mLinePoints[i - 1].left - mLinePoints[i].left, mLinePoints[i - 1].top - mLinePoints[i].top);
345 
346  // getting normal between previous and next point
347  if (i != mLinePoints.size() - 1)
348  normal = _getMiddleLine(mLinePoints[i - 1], mLinePoints[i + 1], mLinePoints[i]);
349  else
350  normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
351 
352  bool edge = false;
353  bool sharp = false;
354  if (normal == FloatPoint() /*|| len(normal.left, normal.top) > mLineWidth * 2*/)
355  {
356  edge = true;
357  normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
358  }
359  else if (len(normal.left, normal.top) > mLineWidth * 1.5)
360  {
361  sharp = true;
362  normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
363  }
364 
365  // check orientation
366  FloatPoint lineDir = mLinePoints[i] - mLinePoints[i - 1];
367  if (lineDir.left * normal.top - lineDir.top * normal.left < 0)
368  {
369  normal.left = -normal.left;
370  normal.top = -normal.top;
371  }
372 
373  FloatPoint UVoffset(currentLength / mLineLength * vectorU.left, currentLength / mLineLength * vectorU.top);
374 
375  if (draw)
376  {
377  mResultVerticiesPos.push_back(points[0]);
378  mResultVerticiesPos.push_back(points[1]);
379  mResultVerticiesPos.push_back(mLinePoints[i] + normal);
380  mResultVerticiesUV.push_back(pointsUV[0]);
381  mResultVerticiesUV.push_back(pointsUV[1]);
382  mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
383 
384  mResultVerticiesPos.push_back(points[1]);
385  mResultVerticiesPos.push_back(mLinePoints[i] - normal);
386  mResultVerticiesPos.push_back(mLinePoints[i] + normal);
387  mResultVerticiesUV.push_back(pointsUV[1]);
388  mResultVerticiesUV.push_back(baseVerticiesUV[3] + UVoffset);
389  mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
390  }
391 
392  points[edge ? 1 : 0] = mLinePoints[i] + normal;
393  points[edge ? 0 : 1] = mLinePoints[i] - normal;
394  pointsUV[0] = baseVerticiesUV[0] + UVoffset;
395  pointsUV[1] = baseVerticiesUV[3] + UVoffset;
396 
397  if (sharp)
398  {
399  normal = _getMiddleLine(mLinePoints[i - 1], mLinePoints[i + 1], mLinePoints[i]);
400 
401  float sharpness = len(normal.left, normal.top) / mLineWidth;
402 
403  float length = len(normal.left, normal.top);
404  normal.left *= 2 * mLineWidth / length / (sharpness - 0.5f);
405  normal.top *= 2 * mLineWidth / length / (sharpness - 0.5f);
406 
407  // check orientation
408  lineDir = mLinePoints[i] - mLinePoints[i - 1];
409  if (lineDir.left * normal.top - lineDir.top * normal.left < 0)
410  {
411  normal.left = -normal.left;
412  normal.top = -normal.top;
413  }
414  FloatPoint lineDir1 = mLinePoints[i] - mLinePoints[i - 1];
415  FloatPoint lineDir2 = mLinePoints[i + 1] - mLinePoints[i];
416  if (lineDir1.left * lineDir2.top - lineDir1.top * lineDir2.left > 0)
417  {
418  normal.left = -normal.left;
419  normal.top = -normal.top;
420  }
421 
422  // check orientation
423  FloatPoint normal2 = _getPerpendicular(mLinePoints[i], mLinePoints[i + 1]);
424  lineDir = mLinePoints[i - 1] - mLinePoints[i];
425  if (lineDir.left * normal2.top - lineDir.top * normal2.left < 0)
426  {
427  normal2.left = -normal2.left;
428  normal2.top = -normal2.top;
429  }
430 
431  FloatPoint UVcenter((baseVerticiesUV[0].left + baseVerticiesUV[3].left) / 2, (baseVerticiesUV[0].top + baseVerticiesUV[3].top) / 2);
432 
433  if (draw)
434  {
435  mResultVerticiesPos.push_back(points[0]);
436  mResultVerticiesPos.push_back(mLinePoints[i] + normal);
437  mResultVerticiesPos.push_back(mLinePoints[i]);
438  mResultVerticiesUV.push_back(pointsUV[0]);
439  mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
440  mResultVerticiesUV.push_back(UVcenter + UVoffset);
441 
442  mResultVerticiesPos.push_back(mLinePoints[i] + normal);
443  mResultVerticiesPos.push_back(mLinePoints[i] + normal2);
444  mResultVerticiesPos.push_back(mLinePoints[i]);
445  mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
446  mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
447  mResultVerticiesUV.push_back(UVcenter + UVoffset);
448  }
449 
450  points[0] = mLinePoints[i] + normal2;
451  points[1] = mLinePoints[i] - normal2;
452  pointsUV[0] = baseVerticiesUV[0] + UVoffset;
453  pointsUV[1] = baseVerticiesUV[3] + UVoffset;
454  }
455  }
456 
457 
458 #ifndef MYGUI_NO_POLYGONAL_SKIN_CROPPING
459  // crop triangles
460  IntCoord cropRectangle(
461  mCurrentCoord.left,
462  mCurrentCoord.top,
463  mCurrentCoord.width,
464  mCurrentCoord.height
465  );
466 
467  VectorFloatPoint newResultVerticiesPos;
468  VectorFloatPoint newResultVerticiesUV;
469  newResultVerticiesPos.reserve(mResultVerticiesPos.size());
470  newResultVerticiesUV.reserve(mResultVerticiesPos.size());
471  for (size_t i = 0; i < mResultVerticiesPos.size(); i += 3)
472  {
473  VectorFloatPoint croppedTriangle =
474  geometry_utility::cropPolygon(&mResultVerticiesPos[i], 3, cropRectangle);
475  if (!croppedTriangle.empty())
476  {
477  FloatPoint v0 = mResultVerticiesUV[i + 2] - mResultVerticiesUV[i];
478  FloatPoint v1 = mResultVerticiesUV[i + 1] - mResultVerticiesUV[i];
479 
480  for (size_t j = 1; j < croppedTriangle.size() - 1; ++j)
481  {
482  newResultVerticiesPos.push_back(croppedTriangle[0]);
483  newResultVerticiesPos.push_back(croppedTriangle[j]);
484  newResultVerticiesPos.push_back(croppedTriangle[j + 1]);
485 
486  // calculate UV
487  FloatPoint point;
488  point = geometry_utility::getPositionInsideRect(croppedTriangle[0], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
489  newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
490  point = geometry_utility::getPositionInsideRect(croppedTriangle[j], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
491  newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
492  point = geometry_utility::getPositionInsideRect(croppedTriangle[j + 1], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
493  newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
494  }
495  }
496  }
497  std::swap(mResultVerticiesPos, newResultVerticiesPos);
498  std::swap(mResultVerticiesUV, newResultVerticiesUV);
499 #endif
500 
501 
502  // now calculate widget base offset and then resulting position in screen coordinates
503  const RenderTargetInfo& info = mRenderItem->getRenderTarget()->getInfo();
504  float vertex_left_base = ((info.pixScaleX * (float)(mCroppedParent->getAbsoluteLeft()) + info.hOffset) * 2) - 1;
505  float vertex_top_base = -(((info.pixScaleY * (float)(mCroppedParent->getAbsoluteTop()) + info.vOffset) * 2) - 1);
506 
507  for (size_t i = 0; i < mResultVerticiesPos.size(); ++i)
508  {
509  mResultVerticiesPos[i].left = vertex_left_base + mResultVerticiesPos[i].left * info.pixScaleX * 2;
510  mResultVerticiesPos[i].top = vertex_top_base + mResultVerticiesPos[i].top * info.pixScaleY * -2;
511  }
512  }
513 
515  {
516  // dy, -dx
517  FloatPoint result(_point1.top - _point2.top, -(_point1.left - _point2.left));
518  // normalise
519  float length = len(result.top, result.left);
520  result.left /= length;
521  result.top /= length;
522  result.left *= mLineWidth / 2;
523  result.top *= mLineWidth / 2;
524  return result;
525  }
526 
527  FloatPoint PolygonalSkin::_getMiddleLine(const FloatPoint& _point1, const FloatPoint& _point2, const FloatPoint& _point3)
528  {
529  // bisectrix
530  FloatPoint line1 = _point3 - _point1;
531  FloatPoint line2 = _point3 - _point2;
532  float length = len(line1.top, line1.left);
533  line1.left /= length;
534  line1.top /= length;
535  length = len(line2.top, line2.left);
536  line2.left /= length;
537  line2.top /= length;
538  FloatPoint result = line1 + line2;
539  // normalise
540  length = len(result.top, result.left);
541  if (length < 1e-6)
542  {
543  return _getPerpendicular(_point1, _point2);
544  }
545  result.left /= length;
546  result.top /= length;
547 
548  float cos = result.left * line1.left + result.top * line1.top;
549  float angle = acos(cos);
550 
551  // too sharp angle
552  if (fabs(angle) < 1e-6 /*< 0.2f*/)
553  return FloatPoint();
554 
555  float width = mLineWidth / 2 / sin(angle);
556  result.left *= width;
557  result.top *= width;
558  return result;
559  }
560 
561 } // namespace MyGUI