| 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 "2D/BsImageSprite.h" |
| 4 | #include "2D/BsSpriteManager.h" |
| 5 | #include "Image/BsTexture.h" |
| 6 | #include "Image/BsSpriteTexture.h" |
| 7 | |
| 8 | namespace bs |
| 9 | { |
| 10 | ImageSprite::~ImageSprite() |
| 11 | { |
| 12 | clearMesh(); |
| 13 | } |
| 14 | |
| 15 | void ImageSprite::update(const IMAGE_SPRITE_DESC& desc, UINT64 groupId) |
| 16 | { |
| 17 | if(!SpriteTexture::checkIsLoaded(desc.texture)) |
| 18 | { |
| 19 | clearMesh(); |
| 20 | return; |
| 21 | } |
| 22 | |
| 23 | // Actually generate a mesh |
| 24 | if(mCachedRenderElements.size() < 1) |
| 25 | mCachedRenderElements.resize(1); |
| 26 | |
| 27 | bool useScale9Grid = desc.borderLeft > 0 || desc.borderRight > 0 || |
| 28 | desc.borderTop > 0 || desc.borderBottom > 0; |
| 29 | |
| 30 | UINT32 numQuads = 1; |
| 31 | if(useScale9Grid) |
| 32 | numQuads = 9; |
| 33 | |
| 34 | SpriteRenderElement& renderElem = mCachedRenderElements[0]; |
| 35 | { |
| 36 | UINT32 newNumQuads = numQuads; |
| 37 | if(newNumQuads != renderElem.numQuads) |
| 38 | { |
| 39 | UINT32 oldVertexCount = renderElem.numQuads * 4; |
| 40 | UINT32 oldIndexCount = renderElem.numQuads * 6; |
| 41 | |
| 42 | if(renderElem.vertices != nullptr) bs_deleteN(renderElem.vertices, oldVertexCount); |
| 43 | if(renderElem.uvs != nullptr) bs_deleteN(renderElem.uvs, oldVertexCount); |
| 44 | if(renderElem.indexes != nullptr) bs_deleteN(renderElem.indexes, oldIndexCount); |
| 45 | |
| 46 | renderElem.vertices = bs_newN<Vector2>(newNumQuads * 4); |
| 47 | renderElem.uvs = bs_newN<Vector2>(newNumQuads * 4); |
| 48 | renderElem.indexes = bs_newN<UINT32>(newNumQuads * 6); |
| 49 | renderElem.numQuads = newNumQuads; |
| 50 | } |
| 51 | |
| 52 | const HTexture& tex = desc.texture->getTexture(); |
| 53 | |
| 54 | SpriteMaterialInfo& matInfo = renderElem.matInfo; |
| 55 | matInfo.groupId = groupId; |
| 56 | matInfo.texture = tex; |
| 57 | matInfo.tint = desc.color; |
| 58 | matInfo.animationStartTime = desc.animationStartTime; |
| 59 | |
| 60 | bool animated = desc.texture->getAnimation().count > 1; |
| 61 | if(animated) |
| 62 | matInfo.spriteTexture = desc.texture; |
| 63 | |
| 64 | renderElem.material = SpriteManager::instance().getImageMaterial(desc.transparent, animated); |
| 65 | } |
| 66 | |
| 67 | for(UINT32 i = 0; i < numQuads; i++) |
| 68 | { |
| 69 | renderElem.indexes[i * 6 + 0] = i * 4 + 0; |
| 70 | renderElem.indexes[i * 6 + 1] = i * 4 + 1; |
| 71 | renderElem.indexes[i * 6 + 2] = i * 4 + 2; |
| 72 | renderElem.indexes[i * 6 + 3] = i * 4 + 1; |
| 73 | renderElem.indexes[i * 6 + 4] = i * 4 + 3; |
| 74 | renderElem.indexes[i * 6 + 5] = i * 4 + 2; |
| 75 | } |
| 76 | |
| 77 | Vector2I offset = getAnchorOffset(desc.anchor, desc.width, desc.height); |
| 78 | Vector2 uvOffset = desc.uvOffset; |
| 79 | Vector2 uvScale = desc.uvScale; |
| 80 | |
| 81 | if(useScale9Grid) |
| 82 | { |
| 83 | UINT32 leftBorder = desc.borderLeft; |
| 84 | UINT32 rightBorder = desc.borderRight; |
| 85 | UINT32 topBorder = desc.borderTop; |
| 86 | UINT32 bottomBorder = desc.borderBottom; |
| 87 | |
| 88 | float centerWidth = (float)std::max((INT32)0, (INT32)desc.width - (INT32)leftBorder - (INT32)rightBorder); |
| 89 | float centerHeight = (float)std::max((INT32)0, (INT32)desc.height - (INT32)topBorder - (INT32)bottomBorder); |
| 90 | |
| 91 | float topCenterStart = (float)(offset.x + leftBorder); |
| 92 | float topRightStart = (float)(topCenterStart + centerWidth); |
| 93 | |
| 94 | float middleStart = (float)(offset.y + topBorder); |
| 95 | float bottomStart = (float)(middleStart + centerHeight); |
| 96 | |
| 97 | // Top left |
| 98 | renderElem.vertices[0] = Vector2((float)offset.x, (float)offset.y); |
| 99 | renderElem.vertices[1] = Vector2((float)offset.x + leftBorder, (float)offset.y); |
| 100 | renderElem.vertices[2] = Vector2((float)offset.x, middleStart); |
| 101 | renderElem.vertices[3] = Vector2((float)offset.x + leftBorder, middleStart); |
| 102 | |
| 103 | // Top center |
| 104 | renderElem.vertices[4] = Vector2(topCenterStart, (float)offset.y); |
| 105 | renderElem.vertices[5] = Vector2(topCenterStart + centerWidth, (float)offset.y); |
| 106 | renderElem.vertices[6] = Vector2(topCenterStart, middleStart); |
| 107 | renderElem.vertices[7] = Vector2(topCenterStart + centerWidth, middleStart); |
| 108 | |
| 109 | // Top right |
| 110 | renderElem.vertices[8] = Vector2(topRightStart, (float)offset.y); |
| 111 | renderElem.vertices[9] = Vector2(topRightStart + rightBorder, (float)offset.y); |
| 112 | renderElem.vertices[10] = Vector2(topRightStart, middleStart); |
| 113 | renderElem.vertices[11] = Vector2(topRightStart + rightBorder, middleStart); |
| 114 | |
| 115 | // Middle left |
| 116 | renderElem.vertices[12] = Vector2((float)offset.x, middleStart); |
| 117 | renderElem.vertices[13] = Vector2((float)offset.x + leftBorder, middleStart); |
| 118 | renderElem.vertices[14] = Vector2((float)offset.x, bottomStart); |
| 119 | renderElem.vertices[15] = Vector2((float)offset.x + leftBorder, bottomStart); |
| 120 | |
| 121 | // Middle center |
| 122 | renderElem.vertices[16] = Vector2(topCenterStart, middleStart); |
| 123 | renderElem.vertices[17] = Vector2(topCenterStart + centerWidth, middleStart); |
| 124 | renderElem.vertices[18] = Vector2(topCenterStart, bottomStart); |
| 125 | renderElem.vertices[19] = Vector2(topCenterStart + centerWidth, bottomStart); |
| 126 | |
| 127 | // Middle right |
| 128 | renderElem.vertices[20] = Vector2(topRightStart, middleStart); |
| 129 | renderElem.vertices[21] = Vector2(topRightStart + rightBorder, middleStart); |
| 130 | renderElem.vertices[22] = Vector2(topRightStart, bottomStart); |
| 131 | renderElem.vertices[23] = Vector2(topRightStart + rightBorder, bottomStart); |
| 132 | |
| 133 | // Bottom left |
| 134 | renderElem.vertices[24] = Vector2((float)offset.x, bottomStart); |
| 135 | renderElem.vertices[25] = Vector2((float)offset.x + leftBorder, bottomStart); |
| 136 | renderElem.vertices[26] = Vector2((float)offset.x, bottomStart + bottomBorder); |
| 137 | renderElem.vertices[27] = Vector2((float)offset.x + leftBorder, bottomStart + bottomBorder); |
| 138 | |
| 139 | // Bottom center |
| 140 | renderElem.vertices[28] = Vector2(topCenterStart, bottomStart); |
| 141 | renderElem.vertices[29] = Vector2(topCenterStart + centerWidth, bottomStart); |
| 142 | renderElem.vertices[30] = Vector2(topCenterStart, bottomStart + bottomBorder); |
| 143 | renderElem.vertices[31] = Vector2(topCenterStart + centerWidth, bottomStart + bottomBorder); |
| 144 | |
| 145 | // Bottom right |
| 146 | renderElem.vertices[32] = Vector2(topRightStart, bottomStart); |
| 147 | renderElem.vertices[33] = Vector2(topRightStart + rightBorder, bottomStart); |
| 148 | renderElem.vertices[34] = Vector2(topRightStart, bottomStart + bottomBorder); |
| 149 | renderElem.vertices[35] = Vector2(topRightStart + rightBorder, bottomStart + bottomBorder); |
| 150 | |
| 151 | float invWidth = 1.0f / (float)desc.texture->getTexture()->getProperties().getWidth(); |
| 152 | float invHeight = 1.0f / (float)desc.texture->getTexture()->getProperties().getHeight(); |
| 153 | |
| 154 | float uvLeftBorder = desc.borderLeft * invWidth; |
| 155 | float uvRightBorder = desc.borderRight * invWidth; |
| 156 | float uvTopBorder = desc.borderTop * invHeight; |
| 157 | float uvBottomBorder = desc.borderBottom * invHeight; |
| 158 | |
| 159 | float uvCenterWidth = std::max(0.0f, uvScale.x - uvLeftBorder - uvRightBorder); |
| 160 | float uvCenterHeight = std::max(0.0f, uvScale.y - uvTopBorder - uvBottomBorder); |
| 161 | |
| 162 | float uvTopCenterStart = uvOffset.x + uvLeftBorder; |
| 163 | float uvTopRightStart = uvTopCenterStart + uvCenterWidth; |
| 164 | |
| 165 | float uvMiddleStart = uvOffset.y + uvTopBorder; |
| 166 | float uvBottomStart = uvMiddleStart + uvCenterHeight; |
| 167 | |
| 168 | // UV - Top left |
| 169 | renderElem.uvs[0] = desc.texture->transformUV(Vector2(uvOffset.x, uvOffset.y)); |
| 170 | renderElem.uvs[1] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvOffset.y)); |
| 171 | renderElem.uvs[2] = desc.texture->transformUV(Vector2(uvOffset.x, uvOffset.y + uvTopBorder)); |
| 172 | renderElem.uvs[3] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvOffset.y + uvTopBorder)); |
| 173 | |
| 174 | // UV - Top center |
| 175 | renderElem.uvs[4] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvOffset.y)); |
| 176 | renderElem.uvs[5] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvOffset.y)); |
| 177 | renderElem.uvs[6] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvOffset.y + uvTopBorder)); |
| 178 | renderElem.uvs[7] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvOffset.y + uvTopBorder)); |
| 179 | |
| 180 | // UV - Top right |
| 181 | renderElem.uvs[8] = desc.texture->transformUV(Vector2(uvTopRightStart, uvOffset.y)); |
| 182 | renderElem.uvs[9] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvOffset.y)); |
| 183 | renderElem.uvs[10] = desc.texture->transformUV(Vector2(uvTopRightStart, uvOffset.y + uvTopBorder)); |
| 184 | renderElem.uvs[11] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvOffset.y + uvTopBorder)); |
| 185 | |
| 186 | // UV - Middle left |
| 187 | renderElem.uvs[12] = desc.texture->transformUV(Vector2(uvOffset.x, uvMiddleStart)); |
| 188 | renderElem.uvs[13] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvMiddleStart)); |
| 189 | renderElem.uvs[14] = desc.texture->transformUV(Vector2(uvOffset.x, uvMiddleStart + uvCenterHeight)); |
| 190 | renderElem.uvs[15] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvMiddleStart + uvCenterHeight)); |
| 191 | |
| 192 | // UV - Middle center |
| 193 | renderElem.uvs[16] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvMiddleStart)); |
| 194 | renderElem.uvs[17] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvMiddleStart)); |
| 195 | renderElem.uvs[18] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvMiddleStart + uvCenterHeight)); |
| 196 | renderElem.uvs[19] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvMiddleStart + uvCenterHeight)); |
| 197 | |
| 198 | // UV - Middle right |
| 199 | renderElem.uvs[20] = desc.texture->transformUV(Vector2(uvTopRightStart, uvMiddleStart)); |
| 200 | renderElem.uvs[21] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvMiddleStart)); |
| 201 | renderElem.uvs[22] = desc.texture->transformUV(Vector2(uvTopRightStart, uvMiddleStart + uvCenterHeight)); |
| 202 | renderElem.uvs[23] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvMiddleStart + uvCenterHeight)); |
| 203 | |
| 204 | // UV - Bottom left |
| 205 | renderElem.uvs[24] = desc.texture->transformUV(Vector2(uvOffset.x, uvBottomStart)); |
| 206 | renderElem.uvs[25] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvBottomStart)); |
| 207 | renderElem.uvs[26] = desc.texture->transformUV(Vector2(uvOffset.x, uvBottomStart + uvBottomBorder)); |
| 208 | renderElem.uvs[27] = desc.texture->transformUV(Vector2(uvOffset.x + uvLeftBorder, uvBottomStart + uvBottomBorder)); |
| 209 | |
| 210 | // UV - Bottom center |
| 211 | renderElem.uvs[28] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvBottomStart)); |
| 212 | renderElem.uvs[29] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvBottomStart)); |
| 213 | renderElem.uvs[30] = desc.texture->transformUV(Vector2(uvTopCenterStart, uvBottomStart + uvBottomBorder)); |
| 214 | renderElem.uvs[31] = desc.texture->transformUV(Vector2(uvTopCenterStart + uvCenterWidth, uvBottomStart + uvBottomBorder)); |
| 215 | |
| 216 | // UV - Bottom right |
| 217 | renderElem.uvs[32] = desc.texture->transformUV(Vector2(uvTopRightStart, uvBottomStart)); |
| 218 | renderElem.uvs[33] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvBottomStart)); |
| 219 | renderElem.uvs[34] = desc.texture->transformUV(Vector2(uvTopRightStart, uvBottomStart + uvBottomBorder)); |
| 220 | renderElem.uvs[35] = desc.texture->transformUV(Vector2(uvTopRightStart + uvRightBorder, uvBottomStart + uvBottomBorder)); |
| 221 | } |
| 222 | else |
| 223 | { |
| 224 | renderElem.vertices[0] = Vector2((float)offset.x, (float)offset.y); |
| 225 | renderElem.vertices[1] = Vector2((float)offset.x + desc.width, (float)offset.y); |
| 226 | renderElem.vertices[2] = Vector2((float)offset.x, (float)offset.y + desc.height); |
| 227 | renderElem.vertices[3] = Vector2((float)offset.x + desc.width, (float)offset.y + desc.height); |
| 228 | |
| 229 | renderElem.uvs[0] = desc.texture->transformUV(Vector2(uvOffset.x, uvOffset.y)); |
| 230 | renderElem.uvs[1] = desc.texture->transformUV(Vector2(uvOffset.x + uvScale.x, uvOffset.y)); |
| 231 | renderElem.uvs[2] = desc.texture->transformUV(Vector2(uvOffset.x, uvOffset.y + uvScale.y)); |
| 232 | renderElem.uvs[3] = desc.texture->transformUV(Vector2(uvOffset.x + uvScale.x, uvOffset.y + uvScale.y)); |
| 233 | } |
| 234 | |
| 235 | updateBounds(); |
| 236 | } |
| 237 | |
| 238 | void ImageSprite::clearMesh() |
| 239 | { |
| 240 | for (auto& renderElem : mCachedRenderElements) |
| 241 | { |
| 242 | UINT32 vertexCount = renderElem.numQuads * 4; |
| 243 | UINT32 indexCount = renderElem.numQuads * 6; |
| 244 | |
| 245 | if (renderElem.vertices != nullptr) |
| 246 | { |
| 247 | bs_deleteN(renderElem.vertices, vertexCount); |
| 248 | renderElem.vertices = nullptr; |
| 249 | } |
| 250 | |
| 251 | if (renderElem.uvs != nullptr) |
| 252 | { |
| 253 | bs_deleteN(renderElem.uvs, vertexCount); |
| 254 | renderElem.uvs = nullptr; |
| 255 | } |
| 256 | |
| 257 | if (renderElem.indexes != nullptr) |
| 258 | { |
| 259 | bs_deleteN(renderElem.indexes, indexCount); |
| 260 | renderElem.indexes = nullptr; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | mCachedRenderElements.clear(); |
| 265 | updateBounds(); |
| 266 | } |
| 267 | |
| 268 | Vector2 ImageSprite::getTextureUVScale(Vector2I sourceSize, Vector2I destSize, TextureScaleMode scaleMode) |
| 269 | { |
| 270 | Vector2 uvScale = Vector2(1.0f, 1.0f); |
| 271 | |
| 272 | switch (scaleMode) |
| 273 | { |
| 274 | case TextureScaleMode::ScaleToFit: |
| 275 | uvScale.x = sourceSize.x / (float)destSize.x; |
| 276 | uvScale.y = sourceSize.y / (float)destSize.y; |
| 277 | |
| 278 | if (uvScale.x > uvScale.y) |
| 279 | { |
| 280 | uvScale.x = 1.0f; |
| 281 | uvScale.y = (destSize.y * (sourceSize.y / (float)sourceSize.x)) / destSize.x; |
| 282 | } |
| 283 | else |
| 284 | { |
| 285 | uvScale.x = (destSize.x * (sourceSize.x / (float)sourceSize.y)) / destSize.y; |
| 286 | uvScale.y = 1.0f; |
| 287 | } |
| 288 | |
| 289 | break; |
| 290 | case TextureScaleMode::CropToFit: |
| 291 | uvScale.x = sourceSize.x / (float)destSize.x; |
| 292 | uvScale.y = sourceSize.y / (float)destSize.y; |
| 293 | |
| 294 | if (uvScale.x > uvScale.y) |
| 295 | { |
| 296 | uvScale.x = (destSize.x * (sourceSize.x / (float)sourceSize.y)) / destSize.y; |
| 297 | uvScale.y = 1.0f; |
| 298 | } |
| 299 | else |
| 300 | { |
| 301 | uvScale.x = 1.0f; |
| 302 | uvScale.y = (destSize.y * (sourceSize.y / (float)sourceSize.x)) / destSize.x; |
| 303 | } |
| 304 | |
| 305 | break; |
| 306 | case TextureScaleMode::RepeatToFit: |
| 307 | uvScale.x = destSize.x / (float)sourceSize.x; |
| 308 | uvScale.y = destSize.y / (float)sourceSize.y; |
| 309 | break; |
| 310 | case TextureScaleMode::StretchToFit: |
| 311 | // Do nothing, (1.0f, 1.0f) is the default UV scale |
| 312 | break; |
| 313 | default: |
| 314 | break; |
| 315 | } |
| 316 | |
| 317 | return uvScale; |
| 318 | } |
| 319 | } |