MyGUI
3.2.0
|
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