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