1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "GUI/BsGUICanvas.h" |
4 | #include "GUI/BsGUISkin.h" |
5 | #include "Image/BsSpriteTexture.h" |
6 | #include "GUI/BsGUIDimensions.h" |
7 | #include "GUI/BsGUITexture.h" |
8 | #include "Utility/BsShapeMeshes2D.h" |
9 | #include "2D/BsSpriteManager.h" |
10 | #include "2D/BsSpriteMaterials.h" |
11 | #include "Mesh/BsMeshUtility.h" |
12 | #include "Error/BsException.h" |
13 | |
14 | namespace bs |
15 | { |
16 | const float GUICanvas::LINE_SMOOTH_BORDER_WIDTH = 3.0f; |
17 | |
18 | const String& GUICanvas::getGUITypeName() |
19 | { |
20 | static String name = "Canvas" ; |
21 | return name; |
22 | } |
23 | |
24 | GUICanvas::GUICanvas(const String& styleName, const GUIDimensions& dimensions) |
25 | : GUIElement(styleName, dimensions), mNumRenderElements(0), mDepthRange(1), mLastOffset(BsZero) |
26 | , mForceTriangleBuild(false) |
27 | { |
28 | |
29 | } |
30 | |
31 | GUICanvas::~GUICanvas() |
32 | { |
33 | clear(); |
34 | } |
35 | |
36 | GUICanvas* GUICanvas::create(const GUIOptions& options, const String& styleName) |
37 | { |
38 | return new (bs_alloc<GUICanvas>()) GUICanvas(getStyleName<GUICanvas>(styleName), GUIDimensions::create(options)); |
39 | } |
40 | |
41 | GUICanvas* GUICanvas::create(const String& styleName) |
42 | { |
43 | return new (bs_alloc<GUICanvas>()) GUICanvas(getStyleName<GUICanvas>(styleName), GUIDimensions::create()); |
44 | } |
45 | |
46 | void GUICanvas::drawLine(const Vector2I& a, const Vector2I& b, const Color& color, UINT8 depth) |
47 | { |
48 | drawPolyLine({ a, b }, color, depth); |
49 | } |
50 | |
51 | void GUICanvas::drawPolyLine(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth) |
52 | { |
53 | if(vertices.size() < 2) |
54 | return; |
55 | |
56 | mElements.push_back(CanvasElement()); |
57 | CanvasElement& element = mElements.back(); |
58 | |
59 | element.type = CanvasElementType::Line; |
60 | element.color = color; |
61 | element.dataId = (UINT32)mTriangleElementData.size(); |
62 | element.vertexStart = (UINT32)mVertexData.size(); |
63 | element.numVertices = (UINT32)vertices.size(); |
64 | element.depth = depth; |
65 | |
66 | mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1)); |
67 | |
68 | mTriangleElementData.push_back(TriangleElementData()); |
69 | TriangleElementData& elemData = mTriangleElementData.back(); |
70 | elemData.matInfo.groupId = 0; |
71 | elemData.matInfo.tint = color; |
72 | |
73 | for (auto& vertex : vertices) |
74 | { |
75 | Vector2 point = Vector2((float)vertex.x, (float)vertex.y); |
76 | point += Vector2(0.5f, 0.5f); // Offset to the middle of the pixel |
77 | |
78 | mVertexData.push_back(point); |
79 | } |
80 | |
81 | mForceTriangleBuild = true; |
82 | _markContentAsDirty(); |
83 | } |
84 | |
85 | void GUICanvas::drawTexture(const HSpriteTexture& texture, const Rect2I& area, TextureScaleMode scaleMode, |
86 | const Color& color, UINT8 depth) |
87 | { |
88 | mElements.push_back(CanvasElement()); |
89 | CanvasElement& element = mElements.back(); |
90 | |
91 | element.type = CanvasElementType::Image; |
92 | element.color = color; |
93 | element.dataId = (UINT32)mImageData.size(); |
94 | element.scaleMode = scaleMode; |
95 | element.imageSprite = bs_new<ImageSprite>(); |
96 | element.depth = depth; |
97 | |
98 | mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1)); |
99 | |
100 | mImageData.push_back({ texture, area }); |
101 | _markContentAsDirty(); |
102 | } |
103 | |
104 | void GUICanvas::drawTriangleStrip(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth) |
105 | { |
106 | if (vertices.size() < 3) |
107 | { |
108 | LOGWRN("Invalid number of vertices. Ignoring call." ); |
109 | return; |
110 | } |
111 | |
112 | mElements.push_back(CanvasElement()); |
113 | CanvasElement& element = mElements.back(); |
114 | |
115 | element.type = CanvasElementType::Triangle; |
116 | element.color = color; |
117 | element.dataId = (UINT32)mTriangleElementData.size(); |
118 | element.vertexStart = (UINT32)mVertexData.size(); |
119 | element.numVertices = (UINT32)(vertices.size() - 2) * 3; |
120 | element.depth = depth; |
121 | |
122 | mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1)); |
123 | |
124 | // Convert strip to list |
125 | for(UINT32 i = 2; i < (UINT32)vertices.size(); i++) |
126 | { |
127 | if (i % 2 == 0) |
128 | { |
129 | mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f)); |
130 | mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f)); |
131 | mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f)); |
132 | } |
133 | else |
134 | { |
135 | mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f)); |
136 | mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f)); |
137 | mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f)); |
138 | } |
139 | } |
140 | |
141 | mTriangleElementData.push_back(TriangleElementData()); |
142 | TriangleElementData& elemData = mTriangleElementData.back(); |
143 | elemData.matInfo.groupId = 0; |
144 | elemData.matInfo.tint = color; |
145 | |
146 | mForceTriangleBuild = true; |
147 | _markContentAsDirty(); |
148 | } |
149 | |
150 | void GUICanvas::drawTriangleList(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth) |
151 | { |
152 | if (vertices.size() < 3 || vertices.size() % 3 != 0) |
153 | { |
154 | LOGWRN("Invalid number of vertices. Ignoring call." ); |
155 | return; |
156 | } |
157 | |
158 | mElements.push_back(CanvasElement()); |
159 | CanvasElement& element = mElements.back(); |
160 | |
161 | element.type = CanvasElementType::Triangle; |
162 | element.color = color; |
163 | element.dataId = (UINT32)mTriangleElementData.size(); |
164 | element.vertexStart = (UINT32)mVertexData.size(); |
165 | element.numVertices = (UINT32)vertices.size(); |
166 | element.depth = depth; |
167 | |
168 | mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1)); |
169 | |
170 | for (auto& vertex : vertices) |
171 | mVertexData.push_back(Vector2((float)vertex.x + 0.5f, (float)vertex.y + 0.5f)); |
172 | |
173 | mTriangleElementData.push_back(TriangleElementData()); |
174 | TriangleElementData& elemData = mTriangleElementData.back(); |
175 | elemData.matInfo.groupId = 0; |
176 | elemData.matInfo.tint = color; |
177 | |
178 | mForceTriangleBuild = true; |
179 | _markContentAsDirty(); |
180 | } |
181 | |
182 | void GUICanvas::drawText(const String& text, const Vector2I& position, const HFont& font, UINT32 size, |
183 | const Color& color, UINT8 depth) |
184 | { |
185 | mElements.push_back(CanvasElement()); |
186 | CanvasElement& element = mElements.back(); |
187 | |
188 | element.type = CanvasElementType::Text; |
189 | element.color = color; |
190 | element.dataId = (UINT32)mTextData.size(); |
191 | element.size = size; |
192 | element.textSprite = bs_new<TextSprite>(); |
193 | element.depth = depth; |
194 | |
195 | mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1)); |
196 | |
197 | mTextData.push_back({ text, font, position }); |
198 | _markContentAsDirty(); |
199 | } |
200 | |
201 | void GUICanvas::clear() |
202 | { |
203 | for (auto& element : mElements) |
204 | { |
205 | if(element.type == CanvasElementType::Image && element.imageSprite != nullptr) |
206 | bs_delete(element.imageSprite); |
207 | |
208 | if (element.type == CanvasElementType::Text && element.textSprite != nullptr) |
209 | bs_delete(element.textSprite); |
210 | } |
211 | |
212 | mElements.clear(); |
213 | mNumRenderElements = 0; |
214 | mDepthRange = 1; |
215 | |
216 | mVertexData.clear(); |
217 | mImageData.clear(); |
218 | mTextData.clear(); |
219 | mTriangleElementData.clear(); |
220 | mClippedVertices.clear(); |
221 | mClippedLineVertices.clear(); |
222 | mForceTriangleBuild = false; |
223 | } |
224 | |
225 | UINT32 GUICanvas::_getNumRenderElements() const |
226 | { |
227 | return mNumRenderElements; |
228 | } |
229 | |
230 | UINT32 GUICanvas::_getRenderElementDepth(UINT32 renderElementIdx) const |
231 | { |
232 | const CanvasElement& element = findElement(renderElementIdx); |
233 | return _getDepth() + element.depth; |
234 | } |
235 | |
236 | const SpriteMaterialInfo& GUICanvas::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const |
237 | { |
238 | static const SpriteMaterialInfo defaultMatInfo; |
239 | |
240 | Vector2 offset((float)mLayoutData.area.x, (float)mLayoutData.area.y); |
241 | Rect2I clipRect = mLayoutData.getLocalClipRect(); |
242 | buildAllTriangleElementsIfDirty(offset, clipRect); |
243 | |
244 | const CanvasElement& element = findElement(renderElementIdx); |
245 | renderElementIdx -= element.renderElemStart; |
246 | |
247 | switch (element.type) |
248 | { |
249 | case CanvasElementType::Line: |
250 | *material = SpriteManager::instance().getLineMaterial(); |
251 | return mTriangleElementData[element.dataId].matInfo; |
252 | case CanvasElementType::Image: |
253 | *material = element.imageSprite->getMaterial(0); |
254 | return element.imageSprite->getMaterialInfo(0); |
255 | case CanvasElementType::Text: |
256 | *material = element.imageSprite->getMaterial(renderElementIdx); |
257 | return element.textSprite->getMaterialInfo(renderElementIdx); |
258 | case CanvasElementType::Triangle: |
259 | *material = SpriteManager::instance().getImageMaterial(true); |
260 | return mTriangleElementData[element.dataId].matInfo; |
261 | default: |
262 | *material = nullptr; |
263 | return defaultMatInfo; |
264 | } |
265 | } |
266 | |
267 | void GUICanvas::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const |
268 | { |
269 | Vector2 offset((float)mLayoutData.area.x, (float)mLayoutData.area.y); |
270 | Rect2I clipRect = mLayoutData.getLocalClipRect(); |
271 | buildAllTriangleElementsIfDirty(offset, clipRect); |
272 | |
273 | const CanvasElement& element = findElement(renderElementIdx); |
274 | renderElementIdx -= element.renderElemStart; |
275 | |
276 | switch (element.type) |
277 | { |
278 | case CanvasElementType::Image: |
279 | { |
280 | UINT32 numQuads = element.imageSprite->getNumQuads(renderElementIdx); |
281 | numVertices = numQuads * 4; |
282 | numIndices = numQuads * 6; |
283 | type = GUIMeshType::Triangle; |
284 | break; |
285 | } |
286 | case CanvasElementType::Text: |
287 | { |
288 | UINT32 numQuads = element.textSprite->getNumQuads(renderElementIdx); |
289 | numVertices = numQuads * 4; |
290 | numIndices = numQuads * 6; |
291 | type = GUIMeshType::Triangle; |
292 | break; |
293 | } |
294 | case CanvasElementType::Line: |
295 | numVertices = element.clippedNumVertices; |
296 | numIndices = element.clippedNumVertices; |
297 | type = GUIMeshType::Line; |
298 | break; |
299 | case CanvasElementType::Triangle: |
300 | numVertices = element.clippedNumVertices; |
301 | numIndices = element.clippedNumVertices; |
302 | type = GUIMeshType::Triangle; |
303 | break; |
304 | default: |
305 | numVertices = 0; |
306 | numIndices = 0; |
307 | type = GUIMeshType::Triangle; |
308 | break; |
309 | } |
310 | } |
311 | |
312 | void GUICanvas::updateRenderElementsInternal() |
313 | { |
314 | mNumRenderElements = 0; |
315 | for(auto& element : mElements) |
316 | { |
317 | switch(element.type) |
318 | { |
319 | case CanvasElementType::Image: |
320 | buildImageElement(element); |
321 | element.renderElemStart = mNumRenderElements; |
322 | element.renderElemEnd = element.renderElemStart + element.imageSprite->getNumRenderElements(); |
323 | break; |
324 | case CanvasElementType::Text: |
325 | buildTextElement(element); |
326 | element.renderElemStart = mNumRenderElements; |
327 | element.renderElemEnd = element.renderElemStart + element.textSprite->getNumRenderElements(); |
328 | break; |
329 | case CanvasElementType::Line: |
330 | case CanvasElementType::Triangle: |
331 | element.renderElemStart = mNumRenderElements; |
332 | element.renderElemEnd = element.renderElemStart + 1; |
333 | |
334 | mTriangleElementData[element.dataId].matInfo.groupId = (UINT64)_getParentWidget(); |
335 | |
336 | // Actual mesh build happens when reading from it, because the mesh size varies due to clipping rectangle/offset |
337 | break; |
338 | } |
339 | |
340 | mNumRenderElements = element.renderElemEnd; |
341 | } |
342 | |
343 | GUIElement::updateRenderElementsInternal(); |
344 | } |
345 | |
346 | Vector2I GUICanvas::_getOptimalSize() const |
347 | { |
348 | return Vector2I(10, 10); |
349 | } |
350 | |
351 | void GUICanvas::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset, |
352 | UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const |
353 | { |
354 | UINT8* uvs = vertices + sizeof(Vector2); |
355 | UINT32 indexStride = sizeof(UINT32); |
356 | |
357 | Vector2I offset(mLayoutData.area.x, mLayoutData.area.y); |
358 | Rect2I clipRect = mLayoutData.getLocalClipRect(); |
359 | |
360 | Vector2 floatOffset((float)offset.x, (float)offset.y); |
361 | buildAllTriangleElementsIfDirty(floatOffset, clipRect); |
362 | |
363 | const CanvasElement& element = findElement(renderElementIdx); |
364 | renderElementIdx -= element.renderElemStart; |
365 | |
366 | switch(element.type) |
367 | { |
368 | case CanvasElementType::Image: |
369 | { |
370 | UINT32 vertexStride = sizeof(Vector2) * 2; |
371 | const Rect2I& area = mImageData[element.dataId].area; |
372 | |
373 | offset.x += area.x; |
374 | offset.y += area.y; |
375 | clipRect.x -= area.x; |
376 | clipRect.y -= area.y; |
377 | |
378 | element.imageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices, |
379 | vertexStride, indexStride, renderElementIdx, offset, clipRect); |
380 | } |
381 | break; |
382 | case CanvasElementType::Text: |
383 | { |
384 | UINT32 vertexStride = sizeof(Vector2) * 2; |
385 | const Vector2I& position = mTextData[element.dataId].position; |
386 | offset += position; |
387 | clipRect.x -= position.x; |
388 | clipRect.y -= position.y; |
389 | |
390 | element.textSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices, |
391 | vertexStride, indexStride, renderElementIdx, offset, clipRect); |
392 | } |
393 | break; |
394 | case CanvasElementType::Triangle: |
395 | { |
396 | UINT32 vertexStride = sizeof(Vector2) * 2; |
397 | |
398 | UINT32 startVert = vertexOffset; |
399 | UINT32 startIndex = indexOffset; |
400 | |
401 | UINT32 maxVertIdx = maxNumVerts; |
402 | UINT32 maxIndexIdx = maxNumIndices; |
403 | |
404 | UINT32 numVertices = element.clippedNumVertices; |
405 | UINT32 numIndices = numVertices; |
406 | |
407 | assert((startVert + numVertices) <= maxVertIdx); |
408 | assert((startIndex + numIndices) <= maxIndexIdx); |
409 | |
410 | UINT8* vertDst = vertices + startVert * vertexStride; |
411 | UINT8* uvDst = uvs + startVert * vertexStride; |
412 | UINT32* indexDst = indices + startIndex; |
413 | |
414 | Vector2 zeroUV(BsZero); |
415 | for(UINT32 i = 0; i < element.clippedNumVertices; i++) |
416 | { |
417 | memcpy(vertDst, &mClippedVertices[element.clippedVertexStart + i], sizeof(Vector2)); |
418 | memcpy(uvDst, &zeroUV, sizeof(Vector2)); |
419 | |
420 | vertDst += vertexStride; |
421 | uvDst += vertexStride; |
422 | indexDst[i] = i; |
423 | } |
424 | } |
425 | break; |
426 | case CanvasElementType::Line: |
427 | { |
428 | UINT32 vertexStride = sizeof(Vector2); |
429 | |
430 | UINT32 startVert = vertexOffset; |
431 | UINT32 startIndex = indexOffset; |
432 | |
433 | UINT32 maxVertIdx = maxNumVerts; |
434 | UINT32 maxIndexIdx = maxNumIndices; |
435 | |
436 | UINT32 numVertices = element.clippedNumVertices; |
437 | UINT32 numIndices = numVertices; |
438 | |
439 | assert((startVert + numVertices) <= maxVertIdx); |
440 | assert((startIndex + numIndices) <= maxIndexIdx); |
441 | |
442 | UINT8* vertDst = vertices + startVert * vertexStride; |
443 | UINT32* indexDst = indices + startIndex; |
444 | |
445 | for (UINT32 i = 0; i < element.clippedNumVertices; i++) |
446 | { |
447 | const Vector2& point = mClippedLineVertices[element.clippedVertexStart + i]; |
448 | |
449 | memcpy(vertDst, &point, sizeof(Vector2)); |
450 | |
451 | vertDst += vertexStride; |
452 | indexDst[i] = i; |
453 | } |
454 | } |
455 | break; |
456 | } |
457 | } |
458 | |
459 | void GUICanvas::buildImageElement(const CanvasElement& element) |
460 | { |
461 | assert(element.type == CanvasElementType::Image); |
462 | |
463 | const ImageElementData& imageData = mImageData[element.dataId]; |
464 | |
465 | IMAGE_SPRITE_DESC desc; |
466 | desc.width = imageData.area.width; |
467 | desc.height = imageData.area.height; |
468 | |
469 | desc.transparent = true; |
470 | desc.color = element.color; |
471 | |
472 | Vector2I textureSize; |
473 | if (SpriteTexture::checkIsLoaded(imageData.texture)) |
474 | { |
475 | desc.texture = imageData.texture; |
476 | textureSize.x = desc.texture->getWidth(); |
477 | textureSize.y = desc.texture->getHeight(); |
478 | } |
479 | |
480 | Vector2I destSize(mLayoutData.area.width, mLayoutData.area.height); |
481 | desc.uvScale = ImageSprite::getTextureUVScale(textureSize, destSize, element.scaleMode); |
482 | |
483 | element.imageSprite->update(desc, (UINT64)_getParentWidget()); |
484 | } |
485 | |
486 | void GUICanvas::buildTextElement(const CanvasElement& element) |
487 | { |
488 | assert(element.type == CanvasElementType::Text); |
489 | |
490 | const TextElementData& textData = mTextData[element.dataId]; |
491 | |
492 | TEXT_SPRITE_DESC desc; |
493 | desc.font = textData.font; |
494 | desc.fontSize = element.size; |
495 | desc.text = textData.string; |
496 | desc.color = element.color; |
497 | |
498 | element.textSprite->update(desc, (UINT64)_getParentWidget()); |
499 | } |
500 | |
501 | void GUICanvas::buildTriangleElement(const CanvasElement& element, const Vector2& offset, const Rect2I& clipRect) const |
502 | { |
503 | assert(element.type == CanvasElementType::Triangle || element.type == CanvasElementType::Line); |
504 | |
505 | if (element.type == CanvasElementType::Triangle) |
506 | { |
507 | UINT8* verticesToClip = (UINT8*)&mVertexData[element.vertexStart]; |
508 | UINT32 trianglesToClip = element.numVertices / 3; |
509 | |
510 | auto writeCallback = [&](Vector2* vertices, Vector2* uvs, UINT32 count) |
511 | { |
512 | for (UINT32 i = 0; i < count; i++) |
513 | mClippedVertices.push_back(vertices[i] + offset); |
514 | |
515 | element.clippedNumVertices += count; |
516 | }; |
517 | |
518 | element.clippedVertexStart = (UINT32)mClippedVertices.size(); |
519 | element.clippedNumVertices = 0; |
520 | |
521 | ImageSprite::clipTrianglesToRect(verticesToClip, nullptr, trianglesToClip, sizeof(Vector2), clipRect, |
522 | writeCallback); |
523 | } |
524 | else |
525 | { |
526 | UINT32 numLines = element.numVertices - 1; |
527 | const Vector2* linePoints = &mVertexData[element.vertexStart]; |
528 | |
529 | struct Plane2D |
530 | { |
531 | Plane2D(const Vector2& normal, float d) |
532 | :normal(normal), d(d) |
533 | { } |
534 | |
535 | Vector2 normal; |
536 | float d; |
537 | }; |
538 | |
539 | std::array<Plane2D, 4> clipPlanes = |
540 | {{ |
541 | Plane2D(Vector2(1.0f, 0.0f), (float)clipRect.x), |
542 | Plane2D(Vector2(-1.0f, 0.0f), (float)-(clipRect.x + (INT32)clipRect.width)), |
543 | Plane2D(Vector2(0.0f, 1.0f), (float)clipRect.y), |
544 | Plane2D(Vector2(0.0f, -1.0f), (float)-(clipRect.y + (INT32)clipRect.height)) |
545 | }}; |
546 | |
547 | element.clippedVertexStart = (UINT32)mClippedLineVertices.size(); |
548 | element.clippedNumVertices = 0; |
549 | |
550 | for (UINT32 i = 0; i < numLines; i++) |
551 | { |
552 | Vector2 a = linePoints[i]; |
553 | Vector2 b = linePoints[i + 1]; |
554 | |
555 | bool isVisible = true; |
556 | for(UINT32 j = 0; j < (UINT32)clipPlanes.size(); j++) |
557 | { |
558 | const Plane2D& plane = clipPlanes[j]; |
559 | float d0 = plane.normal.dot(a) - plane.d; |
560 | float d1 = plane.normal.dot(b) - plane.d; |
561 | |
562 | // Line not visible |
563 | if (d0 <= 0 && d1 <= 0) |
564 | { |
565 | isVisible = false; |
566 | break; |
567 | } |
568 | |
569 | // Line visible completely |
570 | if (d0 >= 0 && d1 >= 0) |
571 | continue; |
572 | |
573 | // The line is split by the plane, compute the point of intersection. |
574 | float t = d0 / (d0 - d1); |
575 | Vector2 intersectPt = (1 - t)*a + t*b; |
576 | |
577 | if (d0 > 0) |
578 | b = intersectPt; |
579 | else |
580 | a = intersectPt; |
581 | } |
582 | |
583 | if (!isVisible) |
584 | continue; |
585 | |
586 | mClippedLineVertices.push_back(a + offset); |
587 | mClippedLineVertices.push_back(b + offset); |
588 | |
589 | element.clippedNumVertices += 2; |
590 | } |
591 | } |
592 | } |
593 | |
594 | void GUICanvas::buildAllTriangleElementsIfDirty(const Vector2& offset, const Rect2I& clipRect) const |
595 | { |
596 | // We need to rebuild if new triangle element(s) were added, or if offset or clip rectangle changed |
597 | bool isDirty = mForceTriangleBuild || (mLastOffset != offset) || (mLastClipRect != clipRect); |
598 | if (!isDirty) |
599 | return; |
600 | |
601 | mClippedVertices.clear(); |
602 | mClippedLineVertices.clear(); |
603 | for(auto& element : mElements) |
604 | { |
605 | if (element.type != CanvasElementType::Triangle && element.type != CanvasElementType::Line) |
606 | continue; |
607 | |
608 | buildTriangleElement(element, offset, clipRect); |
609 | } |
610 | |
611 | mLastOffset = offset; |
612 | mLastClipRect = clipRect; |
613 | mForceTriangleBuild = false; |
614 | } |
615 | |
616 | const GUICanvas::CanvasElement& GUICanvas::findElement(UINT32 renderElementIdx) const |
617 | { |
618 | INT32 start = 0; |
619 | INT32 end = (INT32)(mElements.size() - 1); |
620 | |
621 | while (start <= end) |
622 | { |
623 | INT32 middle = (start + end) / 2; |
624 | const CanvasElement& current = mElements[middle]; |
625 | |
626 | if (renderElementIdx >= current.renderElemStart && renderElementIdx < current.renderElemEnd) |
627 | return current; |
628 | |
629 | if (renderElementIdx < current.renderElemStart) |
630 | end = middle - 1; |
631 | else |
632 | start = middle + 1; |
633 | } |
634 | |
635 | BS_EXCEPT(InvalidParametersException, "Cannot find requested GUI render element." ); |
636 | |
637 | static CanvasElement dummyElement; |
638 | return dummyElement; |
639 | } |
640 | } |