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"
9#include "MyGUI_RenderItem.h"
11#include "MyGUI_RenderManager.h"
13
14namespace 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 {
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
81 }
82
83 void PolygonalSkin::setWidth(float _width)
84 {
85 mLineWidth = _width;
87 }
88
89 void PolygonalSkin::setStroke(size_t _value)
90 {
91 mLineStroke = _value;
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 // растягиваем
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
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
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 float getNodeDepth() const =0
virtual void outOfDate(RenderItem *_item)=0
virtual RenderItem * addToRenderItem(ITexture *_texture, bool _firstQueue, bool _separate)=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)