| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "2D/BsTextSprite.h"
- #include "Math/BsVector2.h"
- #include "Math/BsPlane.h"
- #include "Mesh/BsMeshUtility.h"
- namespace bs
- {
- Sprite::Sprite()
- { }
- Sprite::~Sprite()
- { }
- Rect2I Sprite::getBounds(const Vector2I& offset, const Rect2I& clipRect) const
- {
- Rect2I bounds = mBounds;
- if(clipRect.width > 0 && clipRect.height > 0)
- bounds.clip(clipRect);
- bounds.x += offset.x;
- bounds.y += offset.y;
- return bounds;
- }
- UINT32 Sprite::getNumRenderElements() const
- {
- return (UINT32)mCachedRenderElements.size();
- }
- const SpriteMaterialInfo& Sprite::getMaterialInfo(UINT32 renderElementIdx) const
- {
- return mCachedRenderElements.at(renderElementIdx).matInfo;
- }
- SpriteMaterial* Sprite::getMaterial(UINT32 renderElementIdx) const
- {
- return mCachedRenderElements.at(renderElementIdx).material;
- }
- UINT32 Sprite::getNumQuads(UINT32 renderElementIdx) const
- {
- return mCachedRenderElements.at(renderElementIdx).numQuads;
- }
- UINT32 Sprite::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
- UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx,
- const Vector2I& offset, const Rect2I& clipRect, bool clip) const
- {
- const auto& renderElem = mCachedRenderElements.at(renderElementIdx);
- UINT32 startVert = vertexOffset;
- UINT32 startIndex = indexOffset;
- UINT32 maxVertIdx = maxNumVerts;
- UINT32 maxIndexIdx = maxNumIndices;
- UINT32 numVertices = renderElem.numQuads * 4;
- UINT32 numIndices = renderElem.numQuads * 6;
- assert((startVert + numVertices) <= maxVertIdx);
- assert((startIndex + numIndices) <= maxIndexIdx);
- UINT8* vertDst = vertices + startVert * vertexStride;
- UINT8* uvDst = uv + startVert * vertexStride;
- // TODO - I'm sure this can be done in a more cache friendly way. Profile it later.
- Vector2 vecOffset((float)offset.x, (float)offset.y);
- if(clip)
- {
- for(UINT32 i = 0; i < renderElem.numQuads; i++)
- {
- UINT8* vecStart = vertDst;
- UINT8* uvStart = uvDst;
- UINT32 vertIdx = i * 4;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 0], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 0], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 1], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 1], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 2], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 2], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 3], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 3], sizeof(Vector2));
- clipQuadsToRect(vecStart, uvStart, 1, vertexStride, clipRect);
- vertDst = vecStart;
- Vector2* curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- uvDst += vertexStride;
- }
- }
- else
- {
- for(UINT32 i = 0; i < renderElem.numQuads; i++)
- {
- UINT8* vecStart = vertDst;
- UINT32 vertIdx = i * 4;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 0], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 0], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 1], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 1], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 2], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 2], sizeof(Vector2));
- vertDst += vertexStride;
- uvDst += vertexStride;
- memcpy(vertDst, &renderElem.vertices[vertIdx + 3], sizeof(Vector2));
- memcpy(uvDst, &renderElem.uvs[vertIdx + 3], sizeof(Vector2));
- vertDst = vecStart;
- Vector2* curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- curVec = (Vector2*)vertDst;
- *curVec += vecOffset;
- vertDst += vertexStride;
- uvDst += vertexStride;
- }
- }
- if(indices != nullptr)
- memcpy(&indices[startIndex], renderElem.indexes, numIndices * sizeof(UINT32));
- return renderElem.numQuads;
- }
- Vector2I Sprite::getAnchorOffset(SpriteAnchor anchor, UINT32 width, UINT32 height)
- {
- switch(anchor)
- {
- case SA_TopLeft:
- return -Vector2I(0, 0);
- case SA_TopCenter:
- return -Vector2I(width / 2, 0);
- case SA_TopRight:
- return -Vector2I(width, 0);
- case SA_MiddleLeft:
- return -Vector2I(0, height / 2);
- case SA_MiddleCenter:
- return -Vector2I(width / 2, height / 2);
- case SA_MiddleRight:
- return -Vector2I(width, height / 2);
- case SA_BottomLeft:
- return -Vector2I(0, height);
- case SA_BottomCenter:
- return -Vector2I(width / 2, height);
- case SA_BottomRight:
- return -Vector2I(width, height);
- }
- return Vector2I();
- }
- void Sprite::updateBounds() const
- {
- Vector2 min;
- Vector2 max;
- // Find starting point
- bool foundStartingPoint = false;
- for(auto& renderElem : mCachedRenderElements)
- {
- if(renderElem.vertices != nullptr && renderElem.numQuads > 0)
- {
- min = renderElem.vertices[0];
- max = renderElem.vertices[0];
- foundStartingPoint = true;
- break;
- }
- }
- if(!foundStartingPoint)
- {
- mBounds = Rect2I(0, 0, 0, 0);
- return;
- }
- // Calculate bounds
- for(auto& renderElem : mCachedRenderElements)
- {
- if(renderElem.vertices != nullptr && renderElem.numQuads > 0)
- {
- UINT32 vertexCount = renderElem.numQuads * 4;
- for(UINT32 i = 0; i < vertexCount; i++)
- {
- min = Vector2::min(min, renderElem.vertices[i]);
- max = Vector2::max(max, renderElem.vertices[i]);
- }
- }
- }
- mBounds = Rect2I((int)min.x, (int)min.y, (int)(max.x - min.x), (int)(max.y - min.y));
- }
- // This will only properly clip an array of quads
- // Vertices in the quad must be in a specific order: top left, top right, bottom left, bottom right
- // (0, 0) represents top left of the screen
- void Sprite::clipQuadsToRect(UINT8* vertices, UINT8* uv, UINT32 numQuads, UINT32 vertStride, const Rect2I& clipRect)
- {
- float left = (float)clipRect.x;
- float right = (float)clipRect.x + clipRect.width;
- float top = (float)clipRect.y;
- float bottom = (float)clipRect.y + clipRect.height;
- for(UINT32 i = 0; i < numQuads; i++)
- {
- Vector2* vecA = (Vector2*)(vertices);
- Vector2* vecB = (Vector2*)(vertices + vertStride);
- Vector2* vecC = (Vector2*)(vertices + vertStride * 2);
- Vector2* vecD = (Vector2*)(vertices + vertStride * 3);
- Vector2* uvA = (Vector2*)(uv);
- Vector2* uvB = (Vector2*)(uv + vertStride);
- Vector2* uvC = (Vector2*)(uv + vertStride * 2);
- Vector2* uvD = (Vector2*)(uv + vertStride * 3);
- // Attempt to skip those that are definitely not clipped
- if(vecA->x >= left && vecB->x <= right &&
- vecA->y >= top && vecC->y <= bottom)
- {
- continue;
- }
- // TODO - Skip those that are 100% clipped as well
- float du = (uvB->x - uvA->x) / (vecB->x - vecA->x);
- float dv = (uvA->y - uvC->y) / (vecA->y - vecD->y);
- if(right < left)
- std::swap(left, right);
- if(bottom < top)
- std::swap(bottom, top);
- // Clip left
- float newLeft = Math::clamp(vecA->x, left, right);
- float uvLeftOffset = (newLeft - vecA->x) * du;
- // Clip right
- float newRight = Math::clamp(vecB->x, left, right);
- float uvRightOffset = (vecB->x - newRight) * du;
- // Clip top
- float newTop = Math::clamp(vecA->y, top, bottom);
- float uvTopOffset = (newTop - vecA->y) * dv;
- // Clip bottom
- float newBottom = Math::clamp(vecC->y, top, bottom);
- float uvBottomOffset = (vecC->y - newBottom) * dv;
- vecA->x = newLeft;
- vecC->x = newLeft;
- vecB->x = newRight;
- vecD->x = newRight;
- vecA->y = newTop;
- vecB->y = newTop;
- vecC->y = newBottom;
- vecD->y = newBottom;
-
- uvA->x += uvLeftOffset;
- uvC->x += uvLeftOffset;
- uvB->x -= uvRightOffset;
- uvD->x -= uvRightOffset;
- uvA->y += uvTopOffset;
- uvB->y += uvTopOffset;
- uvC->y -= uvBottomOffset;
- uvD->y -= uvBottomOffset;
- vertices += vertStride * 4;
- uv += vertStride * 4;
- }
- }
- void Sprite::clipTrianglesToRect(UINT8* vertices, UINT8* uv, UINT32 numTris, UINT32 vertStride, const Rect2I& clipRect,
- const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback)
- {
- Vector<Plane> clipPlanes =
- {
- Plane(Vector3(1.0f, 0.0f, 0.0f), (float)clipRect.x),
- Plane(Vector3(-1.0f, 0.0f, 0.0f), (float)-(clipRect.x + (INT32)clipRect.width)),
- Plane(Vector3(0.0f, 1.0f, 0.0f), (float)clipRect.y),
- Plane(Vector3(0.0f, -1.0f, 0.0f), (float)-(clipRect.y + (INT32)clipRect.height))
- };
- MeshUtility::clip2D(vertices, uv, numTris, vertStride, clipPlanes, writeCallback);
- }
- }
|