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