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