MyGUI  3.2.1
MyGUI_PolygonalSkin.cpp
Go to the documentation of this file.
00001 /*
00002  * This source file is part of MyGUI. For the latest info, see http://mygui.info/
00003  * Distributed under the MIT License
00004  * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT)
00005  */
00006 
00007 #include "MyGUI_Precompiled.h"
00008 #include "MyGUI_PolygonalSkin.h"
00009 #include "MyGUI_RenderItem.h"
00010 #include "MyGUI_CommonStateInfo.h"
00011 #include "MyGUI_RenderManager.h"
00012 #include "MyGUI_GeometryUtility.h"
00013 
00014 namespace MyGUI
00015 {
00016 
00017     PolygonalSkin::PolygonalSkin() :
00018         mGeometryOutdated(false),
00019         mLineWidth(1.0f),
00020         mLineStroke(0),
00021         mLineLength(0.0f),
00022         mVertexCount(VertexQuad::VertexCount),
00023         mEmptyView(false),
00024         mCurrentColour(0xFFFFFFFF),
00025         mNode(nullptr),
00026         mRenderItem(nullptr)
00027     {
00028         mVertexFormat = RenderManager::getInstance().getVertexFormat();
00029     }
00030 
00031     PolygonalSkin::~PolygonalSkin()
00032     {
00033     }
00034 
00035     inline float len(float x, float y)
00036     {
00037         return sqrt(x * x + y * y);
00038     }
00039 
00040     void PolygonalSkin::setPoints(const std::vector<FloatPoint>& _points)
00041     {
00042         if (_points.size() < 2)
00043         {
00044             mVertexCount = 0;
00045             mResultVerticiesPos.clear();
00046             mResultVerticiesUV.clear();
00047             mLinePoints = _points;
00048             return;
00049         }
00050 
00051         VectorFloatPoint finalPoints;
00052         finalPoints.reserve(_points.size());
00053 
00054         mLineLength = 0.0f;
00055         FloatPoint point = _points[0];
00056         finalPoints.push_back(point);
00057         // ignore repeating points
00058         for (std::vector<FloatPoint>::const_iterator iter = _points.begin() + 1; iter != _points.end(); ++iter)
00059         {
00060             if (point != *iter)
00061             {
00062                 finalPoints.push_back(*iter);
00063                 mLineLength += len(iter->left - point.left, iter->top - point.top);
00064                 point = *iter;
00065             }
00066         }
00067 
00068         mLinePoints = finalPoints;
00069 
00070 #ifdef MYGUI_NO_POLYGONAL_SKIN_CROPPING
00071         size_t count = (mLinePoints.size() - 1) * VertexQuad::VertexCount * 2;
00072 #else
00073         // it's too hard to calculate maximum possible verticies count and worst
00074         // approximation gives 7 times more verticies than in not cropped geometry
00075         // so we multiply count by 2, because this looks enough
00076         size_t count = (mLinePoints.size() - 1) * VertexQuad::VertexCount * 2 * 2;
00077 #endif
00078         if (count > mVertexCount)
00079         {
00080             mVertexCount = count;
00081             if (nullptr != mRenderItem) mRenderItem->reallockDrawItem(this, mVertexCount);
00082         }
00083 
00084         _updateView();
00085     }
00086 
00087     void PolygonalSkin::setWidth(float _width)
00088     {
00089         mLineWidth = _width;
00090         _updateView();
00091     }
00092 
00093     void PolygonalSkin::setStroke(size_t _value)
00094     {
00095         mLineStroke = _value;
00096         _updateView();
00097     }
00098 
00099     void PolygonalSkin::setVisible(bool _visible)
00100     {
00101         if (mVisible == _visible)
00102             return;
00103 
00104         mVisible = _visible;
00105         mGeometryOutdated = true;
00106 
00107         if (nullptr != mNode)
00108             mNode->outOfDate(mRenderItem);
00109     }
00110 
00111     void PolygonalSkin::setAlpha(float _alpha)
00112     {
00113         uint32 alpha = ((uint8)(_alpha * 255) << 24);
00114         mCurrentColour = (mCurrentColour & 0x00FFFFFF) | (alpha & 0xFF000000);
00115 
00116         if (nullptr != mNode)
00117             mNode->outOfDate(mRenderItem);
00118     }
00119 
00120     void PolygonalSkin::_correctView()
00121     {
00122         mGeometryOutdated = true;
00123 
00124         if (nullptr != mNode)
00125             mNode->outOfDate(mRenderItem);
00126     }
00127 
00128     void PolygonalSkin::_setAlign(const IntSize& _oldsize)
00129     {
00130         // необходимо разобраться
00131         bool need_update = true;
00132 
00133         // первоначальное выравнивание
00134         if (mAlign.isHStretch())
00135         {
00136             // растягиваем
00137             mCoord.width = mCoord.width + (mCroppedParent->getWidth() - _oldsize.width);
00138             need_update = true;
00139             mIsMargin = true; // при изменении размеров все пересчитывать
00140         }
00141         else if (mAlign.isRight())
00142         {
00143             // двигаем по правому краю
00144             mCoord.left = mCoord.left + (mCroppedParent->getWidth() - _oldsize.width);
00145             need_update = true;
00146         }
00147         else if (mAlign.isHCenter())
00148         {
00149             // выравнивание по горизонтали без растяжения
00150             mCoord.left = (mCroppedParent->getWidth() - mCoord.width) / 2;
00151             need_update = true;
00152         }
00153 
00154         if (mAlign.isVStretch())
00155         {
00156             // растягиваем
00157             mCoord.height = mCoord.height + (mCroppedParent->getHeight() - _oldsize.height);
00158             need_update = true;
00159             mIsMargin = true; // при изменении размеров все пересчитывать
00160         }
00161         else if (mAlign.isBottom())
00162         {
00163             // двигаем по нижнему краю
00164             mCoord.top = mCoord.top + (mCroppedParent->getHeight() - _oldsize.height);
00165             need_update = true;
00166         }
00167         else if (mAlign.isVCenter())
00168         {
00169             // выравнивание по вертикали без растяжения
00170             mCoord.top = (mCroppedParent->getHeight() - mCoord.height) / 2;
00171             need_update = true;
00172         }
00173 
00174         if (need_update)
00175         {
00176             mCurrentCoord = mCoord;
00177             _updateView();
00178         }
00179     }
00180 
00181     void PolygonalSkin::_updateView()
00182     {
00183         bool margin = _checkMargin();
00184 
00185         mEmptyView = ((0 >= _getViewWidth()) || (0 >= _getViewHeight()));
00186 
00187         mGeometryOutdated = true;
00188 
00189         mCurrentCoord.left = mCoord.left + mMargin.left;
00190         mCurrentCoord.top = mCoord.top + mMargin.top;
00191 
00192         // вьюпорт стал битым
00193         if (margin)
00194         {
00195             // проверка на полный выход за границу
00196             if (_checkOutside())
00197             {
00198                 // запоминаем текущее состояние
00199                 mIsMargin = margin;
00200 
00201                 // обновить перед выходом
00202                 if (nullptr != mNode)
00203                     mNode->outOfDate(mRenderItem);
00204                 return;
00205             }
00206         }
00207 
00208         // мы обрезаны или были обрезаны
00209         if (mIsMargin || margin)
00210         {
00211             mCurrentCoord.width = _getViewWidth();
00212             mCurrentCoord.height = _getViewHeight();
00213         }
00214 
00215         // запоминаем текущее состояние
00216         mIsMargin = margin;
00217 
00218         if (nullptr != mNode)
00219             mNode->outOfDate(mRenderItem);
00220     }
00221 
00222     void PolygonalSkin::createDrawItem(ITexture* _texture, ILayerNode* _node)
00223     {
00224         MYGUI_ASSERT(!mRenderItem, "mRenderItem must be nullptr");
00225 
00226         mNode = _node;
00227         mRenderItem = mNode->addToRenderItem(_texture, true, false);
00228         mRenderItem->addDrawItem(this, mVertexCount);
00229     }
00230 
00231     void PolygonalSkin::destroyDrawItem()
00232     {
00233         MYGUI_ASSERT(mRenderItem, "mRenderItem must be not nullptr");
00234 
00235         mNode = nullptr;
00236         mRenderItem->removeDrawItem(this);
00237         mRenderItem = nullptr;
00238     }
00239 
00240     void PolygonalSkin::doRender()
00241     {
00242         if (!mVisible || mEmptyView)
00243             return;
00244 
00245         bool update = mRenderItem->getCurrentUpdate();
00246         if (update)
00247             mGeometryOutdated = true;
00248 
00249         Vertex* verticies = mRenderItem->getCurrentVertexBuffer();
00250 
00251         float vertex_z = mNode->getNodeDepth();
00252 
00253         if (mGeometryOutdated)
00254         {
00255             _rebuildGeometry();
00256         }
00257 
00258         size_t size = mResultVerticiesPos.size();
00259 
00260         for (size_t i = 0; i < size; ++i)
00261         {
00262             verticies[i].set(mResultVerticiesPos[i].left, mResultVerticiesPos[i].top, vertex_z, mResultVerticiesUV[i].left, mResultVerticiesUV[i].top, mCurrentColour);
00263         }
00264 
00265         mRenderItem->setLastVertexCount(size);
00266     }
00267 
00268     void PolygonalSkin::_setColour(const Colour& _value)
00269     {
00270         uint32 colour = texture_utility::toColourARGB(_value);
00271         texture_utility::convertColour(colour, mVertexFormat);
00272         mCurrentColour = (colour & 0x00FFFFFF) | (mCurrentColour & 0xFF000000);
00273 
00274         if (nullptr != mNode)
00275             mNode->outOfDate(mRenderItem);
00276     }
00277 
00278     void PolygonalSkin::setStateData(IStateInfo* _data)
00279     {
00280         _setUVSet(_data->castType<SubSkinStateInfo>()->getRect());
00281     }
00282 
00283     void PolygonalSkin::_setUVSet(const FloatRect& _rect)
00284     {
00285         mCurrentTexture = _rect;
00286 
00287         mGeometryOutdated = true;
00288 
00289         if (nullptr != mNode)
00290             mNode->outOfDate(mRenderItem);
00291     }
00292 
00293     void PolygonalSkin::_rebuildGeometry()
00294     {
00295         if (mLinePoints.size() < 2)
00296             return;
00297         if (!mRenderItem || !mRenderItem->getRenderTarget())
00298             return;
00299 
00300         mGeometryOutdated = false;
00301 
00302         // using mCurrentCoord as rectangle where we draw polygons
00303 
00304         // base texture coordinates
00305         FloatPoint baseVerticiesUV[4] =
00306         {
00307             FloatPoint(mCurrentTexture.left, mCurrentTexture.top),
00308             FloatPoint(mCurrentTexture.right, mCurrentTexture.top),
00309             FloatPoint(mCurrentTexture.right, mCurrentTexture.bottom),
00310             FloatPoint(mCurrentTexture.left, mCurrentTexture.bottom)
00311         };
00312 
00313         // UV vectors
00314         FloatPoint vectorU = baseVerticiesUV[1] - baseVerticiesUV[0];
00315         FloatPoint vectorV = baseVerticiesUV[3] - baseVerticiesUV[0];
00316 
00317         FloatPoint vertex1;
00318         FloatPoint vertex2;
00319         mResultVerticiesPos.clear();
00320         mResultVerticiesUV.clear();
00321         // add first two verticies
00322         FloatPoint normal = _getPerpendicular(mLinePoints[0], mLinePoints[1]);
00323 
00324         FloatPoint points[2] = {mLinePoints[0] + normal, mLinePoints[0] - normal};
00325         FloatPoint pointsUV[2] = {baseVerticiesUV[0], baseVerticiesUV[3]};
00326 
00327         bool draw = true;
00328         size_t stroke = 0;
00329 
00330         // add other verticies
00331         float currentLength = 0.0f;
00332         for (size_t i = 1; i < mLinePoints.size(); ++i)
00333         {
00334             if (mLineStroke != 0)
00335             {
00336                 stroke++;
00337                 if (stroke == mLineStroke)
00338                 {
00339                     stroke = 0;
00340                     draw = !draw;
00341                 }
00342             }
00343 
00344             currentLength += len(mLinePoints[i - 1].left - mLinePoints[i].left,  mLinePoints[i - 1].top - mLinePoints[i].top);
00345 
00346             // getting normal between previous and next point
00347             if (i != mLinePoints.size() - 1)
00348                 normal = _getMiddleLine(mLinePoints[i - 1], mLinePoints[i + 1], mLinePoints[i]);
00349             else
00350                 normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
00351 
00352             bool edge = false;
00353             bool sharp = false;
00354             if (normal == FloatPoint() /*|| len(normal.left, normal.top) > mLineWidth * 2*/)
00355             {
00356                 edge = true;
00357                 normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
00358             }
00359             else if (len(normal.left, normal.top) > mLineWidth * 1.5)
00360             {
00361                 sharp = true;
00362                 normal = _getPerpendicular(mLinePoints[i - 1], mLinePoints[i]);
00363             }
00364 
00365             // check orientation
00366             FloatPoint lineDir = mLinePoints[i] - mLinePoints[i - 1];
00367             if (lineDir.left * normal.top - lineDir.top * normal.left < 0)
00368             {
00369                 normal.left = -normal.left;
00370                 normal.top = -normal.top;
00371             }
00372 
00373             FloatPoint UVoffset(currentLength / mLineLength * vectorU.left, currentLength / mLineLength * vectorU.top);
00374 
00375             if (draw)
00376             {
00377                 mResultVerticiesPos.push_back(points[0]);
00378                 mResultVerticiesPos.push_back(points[1]);
00379                 mResultVerticiesPos.push_back(mLinePoints[i] + normal);
00380                 mResultVerticiesUV.push_back(pointsUV[0]);
00381                 mResultVerticiesUV.push_back(pointsUV[1]);
00382                 mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
00383 
00384                 mResultVerticiesPos.push_back(points[1]);
00385                 mResultVerticiesPos.push_back(mLinePoints[i] - normal);
00386                 mResultVerticiesPos.push_back(mLinePoints[i] + normal);
00387                 mResultVerticiesUV.push_back(pointsUV[1]);
00388                 mResultVerticiesUV.push_back(baseVerticiesUV[3] + UVoffset);
00389                 mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
00390             }
00391 
00392             points[edge ? 1 : 0] = mLinePoints[i] + normal;
00393             points[edge ? 0 : 1] = mLinePoints[i] - normal;
00394             pointsUV[0] = baseVerticiesUV[0] + UVoffset;
00395             pointsUV[1] = baseVerticiesUV[3] + UVoffset;
00396 
00397             if (sharp)
00398             {
00399                 normal =  _getMiddleLine(mLinePoints[i - 1], mLinePoints[i + 1], mLinePoints[i]);
00400 
00401                 float sharpness = len(normal.left, normal.top) / mLineWidth;
00402 
00403                 float length = len(normal.left, normal.top);
00404                 normal.left *= 2 * mLineWidth / length / (sharpness - 0.5f);
00405                 normal.top *= 2 * mLineWidth / length / (sharpness - 0.5f);
00406 
00407                 // check orientation
00408                 lineDir = mLinePoints[i] - mLinePoints[i - 1];
00409                 if (lineDir.left * normal.top - lineDir.top * normal.left < 0)
00410                 {
00411                     normal.left = -normal.left;
00412                     normal.top = -normal.top;
00413                 }
00414                 FloatPoint lineDir1 = mLinePoints[i] - mLinePoints[i - 1];
00415                 FloatPoint lineDir2 = mLinePoints[i + 1] - mLinePoints[i];
00416                 if (lineDir1.left * lineDir2.top - lineDir1.top * lineDir2.left > 0)
00417                 {
00418                     normal.left = -normal.left;
00419                     normal.top = -normal.top;
00420                 }
00421 
00422                 // check orientation
00423                 FloatPoint normal2 = _getPerpendicular(mLinePoints[i], mLinePoints[i + 1]);
00424                 lineDir = mLinePoints[i - 1] - mLinePoints[i];
00425                 if (lineDir.left * normal2.top - lineDir.top * normal2.left < 0)
00426                 {
00427                     normal2.left = -normal2.left;
00428                     normal2.top = -normal2.top;
00429                 }
00430 
00431                 FloatPoint UVcenter((baseVerticiesUV[0].left + baseVerticiesUV[3].left) / 2, (baseVerticiesUV[0].top + baseVerticiesUV[3].top) / 2);
00432 
00433                 if (draw)
00434                 {
00435                     mResultVerticiesPos.push_back(points[0]);
00436                     mResultVerticiesPos.push_back(mLinePoints[i] + normal);
00437                     mResultVerticiesPos.push_back(mLinePoints[i]);
00438                     mResultVerticiesUV.push_back(pointsUV[0]);
00439                     mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
00440                     mResultVerticiesUV.push_back(UVcenter + UVoffset);
00441 
00442                     mResultVerticiesPos.push_back(mLinePoints[i] + normal);
00443                     mResultVerticiesPos.push_back(mLinePoints[i] + normal2);
00444                     mResultVerticiesPos.push_back(mLinePoints[i]);
00445                     mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
00446                     mResultVerticiesUV.push_back(baseVerticiesUV[0] + UVoffset);
00447                     mResultVerticiesUV.push_back(UVcenter + UVoffset);
00448                 }
00449 
00450                 points[0] = mLinePoints[i] + normal2;
00451                 points[1] = mLinePoints[i] - normal2;
00452                 pointsUV[0] = baseVerticiesUV[0] + UVoffset;
00453                 pointsUV[1] = baseVerticiesUV[3] + UVoffset;
00454             }
00455         }
00456 
00457 
00458 #ifndef MYGUI_NO_POLYGONAL_SKIN_CROPPING
00459         // crop triangles
00460         IntCoord cropRectangle(
00461             mCurrentCoord.left,
00462             mCurrentCoord.top,
00463             mCurrentCoord.width,
00464             mCurrentCoord.height
00465             );
00466 
00467         VectorFloatPoint newResultVerticiesPos;
00468         VectorFloatPoint newResultVerticiesUV;
00469         newResultVerticiesPos.reserve(mResultVerticiesPos.size());
00470         newResultVerticiesUV.reserve(mResultVerticiesPos.size());
00471         for (size_t i = 0; i < mResultVerticiesPos.size(); i += 3)
00472         {
00473             VectorFloatPoint croppedTriangle =
00474                 geometry_utility::cropPolygon(&mResultVerticiesPos[i], 3, cropRectangle);
00475             if (!croppedTriangle.empty())
00476             {
00477                 FloatPoint v0 = mResultVerticiesUV[i + 2] - mResultVerticiesUV[i];
00478                 FloatPoint v1 = mResultVerticiesUV[i + 1] - mResultVerticiesUV[i];
00479 
00480                 for (size_t j = 1; j < croppedTriangle.size() - 1; ++j)
00481                 {
00482                     newResultVerticiesPos.push_back(croppedTriangle[0]);
00483                     newResultVerticiesPos.push_back(croppedTriangle[j]);
00484                     newResultVerticiesPos.push_back(croppedTriangle[j + 1]);
00485 
00486                     // calculate UV
00487                     FloatPoint point;
00488                     point = geometry_utility::getPositionInsideRect(croppedTriangle[0], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
00489                     newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
00490                     point = geometry_utility::getPositionInsideRect(croppedTriangle[j], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
00491                     newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
00492                     point = geometry_utility::getPositionInsideRect(croppedTriangle[j + 1], mResultVerticiesPos[i], mResultVerticiesPos[i + 1], mResultVerticiesPos[i + 2]);
00493                     newResultVerticiesUV.push_back(geometry_utility::getUVFromPositionInsideRect(point, v0, v1, mResultVerticiesUV[i]));
00494                 }
00495             }
00496         }
00497         std::swap(mResultVerticiesPos, newResultVerticiesPos);
00498         std::swap(mResultVerticiesUV, newResultVerticiesUV);
00499 #endif
00500 
00501 
00502         // now calculate widget base offset and then resulting position in screen coordinates
00503         const RenderTargetInfo& info = mRenderItem->getRenderTarget()->getInfo();
00504         float vertex_left_base = ((info.pixScaleX * (float)(mCroppedParent->getAbsoluteLeft()) + info.hOffset) * 2) - 1;
00505         float vertex_top_base = -(((info.pixScaleY * (float)(mCroppedParent->getAbsoluteTop()) + info.vOffset) * 2) - 1);
00506 
00507         for (size_t i = 0; i < mResultVerticiesPos.size(); ++i)
00508         {
00509             mResultVerticiesPos[i].left = vertex_left_base + mResultVerticiesPos[i].left * info.pixScaleX * 2;
00510             mResultVerticiesPos[i].top = vertex_top_base + mResultVerticiesPos[i].top * info.pixScaleY * -2;
00511         }
00512     }
00513 
00514     FloatPoint PolygonalSkin::_getPerpendicular(const FloatPoint& _point1, const FloatPoint& _point2)
00515     {
00516         // dy, -dx
00517         FloatPoint result(_point1.top - _point2.top, -(_point1.left - _point2.left));
00518         // normalise
00519         float length = len(result.top, result.left);
00520         result.left /= length;
00521         result.top /= length;
00522         result.left *= mLineWidth / 2;
00523         result.top *= mLineWidth / 2;
00524         return result;
00525     }
00526 
00527     FloatPoint PolygonalSkin::_getMiddleLine(const FloatPoint& _point1, const FloatPoint& _point2, const FloatPoint& _point3)
00528     {
00529         // bisectrix
00530         FloatPoint line1 = _point3 - _point1;
00531         FloatPoint line2 = _point3 - _point2;
00532         float length = len(line1.top, line1.left);
00533         line1.left /= length;
00534         line1.top /= length;
00535         length = len(line2.top, line2.left);
00536         line2.left /= length;
00537         line2.top /= length;
00538         FloatPoint result = line1 + line2;
00539         // normalise
00540         length = len(result.top, result.left);
00541         if (length < 1e-6)
00542         {
00543             return _getPerpendicular(_point1, _point2);
00544         }
00545         result.left /= length;
00546         result.top /= length;
00547 
00548         float cos = result.left * line1.left + result.top * line1.top;
00549         float angle = acos(cos);
00550 
00551         // too sharp angle
00552         if (fabs(angle) < 1e-6 /*< 0.2f*/)
00553             return FloatPoint();
00554 
00555         float width = mLineWidth / 2 / sin(angle);
00556         result.left *= width;
00557         result.top *= width;
00558         return result;
00559     }
00560 
00561 } // namespace MyGUI