Line data Source code
1 : /**
2 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XL2dSprite.h"
24 :
25 : #include "XLResourceCache.h"
26 : #include "XLTemporaryResource.h"
27 : #include "XLTexture.h"
28 : #include "XLDirector.h"
29 : #include "XLFrameInfo.h"
30 : #include "XL2dFrameContext.h"
31 : #include "XL2dCommandList.h"
32 :
33 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
34 :
35 23456 : Sprite::Sprite() {
36 23456 : _blendInfo = core::BlendInfo(core::BlendFactor::SrcAlpha, core::BlendFactor::OneMinusSrcAlpha, core::BlendOp::Add,
37 : core::BlendFactor::Zero, core::BlendFactor::One, core::BlendOp::Add);
38 23456 : _materialInfo.setBlendInfo(_blendInfo);
39 23456 : _materialInfo.setDepthInfo(core::DepthInfo(false, true, core::CompareOp::Less));
40 23456 : _applyMode = DoNotApply;
41 23456 : }
42 :
43 23544 : Sprite::~Sprite() { }
44 :
45 21965 : bool Sprite::init() {
46 21965 : return Sprite::init(core::SolidTextureName);
47 : }
48 :
49 23450 : bool Sprite::init(StringView textureName) {
50 23450 : if (!DynamicStateNode::init()) {
51 0 : return false;
52 : }
53 :
54 23450 : _textureName = textureName.str<Interface>();
55 23450 : initVertexes();
56 23450 : return true;
57 : }
58 :
59 6 : bool Sprite::init(Rc<Texture> &&texture) {
60 6 : if (!DynamicStateNode::init()) {
61 0 : return false;
62 : }
63 :
64 6 : if (texture) {
65 6 : _texture = move(texture);
66 6 : _isTextureLoaded = _texture->isLoaded();
67 : }
68 :
69 6 : initVertexes();
70 6 : return true;
71 : }
72 :
73 12 : void Sprite::setTexture(StringView textureName) {
74 12 : if (!_running) {
75 6 : if (_texture) {
76 0 : _texture = nullptr;
77 0 : _materialDirty = true;
78 : }
79 6 : _textureName = textureName.str<Interface>();
80 : } else {
81 6 : if (textureName.empty()) {
82 0 : if (_texture) {
83 0 : if (_running) {
84 0 : _texture->onExit(_frameContext);
85 : }
86 0 : _texture = nullptr;
87 0 : _materialDirty = true;
88 : }
89 6 : } else if (!_texture || _texture->getName() != textureName) {
90 6 : if (auto &cache = _director->getResourceCache()) {
91 6 : if (auto tex = cache->acquireTexture(textureName)) {
92 6 : setTexture(move(tex));
93 6 : }
94 : }
95 : }
96 : }
97 12 : }
98 :
99 1481 : void Sprite::setTexture(Rc<Texture> &&tex) {
100 1481 : if (_texture) {
101 16 : if (!tex) {
102 0 : if (_running) {
103 0 : _texture->onExit(_frameContext);
104 : }
105 0 : _texture = nullptr;
106 0 : _textureName.clear();
107 0 : _materialDirty = true;
108 0 : _isTextureLoaded = false;
109 16 : } else if (_texture->getName() != tex->getName()) {
110 16 : if (_running) {
111 16 : _texture->onExit(_frameContext);
112 : }
113 16 : _texture = move(tex);
114 16 : if (_running) {
115 16 : _texture->onEnter(_frameContext);
116 : }
117 16 : _isTextureLoaded = _texture->isLoaded();
118 16 : if (_isTextureLoaded && _textureLoadedCallback) {
119 0 : _textureLoadedCallback();
120 : }
121 16 : _textureName = _texture->getName().str<Interface>();
122 16 : updateBlendAndDepth();
123 16 : _materialDirty = true;
124 : }
125 : } else {
126 1465 : if (tex) {
127 1465 : _texture = move(tex);
128 1465 : if (_running) {
129 0 : _texture->onEnter(_frameContext);
130 : }
131 1465 : _isTextureLoaded = _texture->isLoaded();
132 1465 : if (_isTextureLoaded && _textureLoadedCallback) {
133 0 : _textureLoadedCallback();
134 : }
135 1465 : _textureName = _texture->getName().str<Interface>();
136 1465 : updateBlendAndDepth();
137 1465 : _materialDirty = true;
138 : }
139 : }
140 1481 : }
141 :
142 18 : const Rc<Texture> &Sprite::getTexture() const {
143 18 : return _texture;
144 : }
145 :
146 750 : void Sprite::setLinearGradient(Rc<LinearGradient> &&g) {
147 750 : _linearGradient = move(g);
148 750 : }
149 :
150 6 : const Rc<LinearGradient> &Sprite::getLinearGradient() const {
151 6 : return _linearGradient;
152 : }
153 :
154 6 : void Sprite::setTextureRect(const Rect &rect) {
155 6 : if (!_textureRect.equals(rect)) {
156 6 : _textureRect = rect;
157 6 : _vertexesDirty = true;
158 : }
159 6 : }
160 :
161 247190 : bool Sprite::visitDraw(FrameInfo &frame, NodeFlags parentFlags) {
162 247190 : if (_texture) {
163 247190 : auto loaded = _texture->isLoaded();
164 247190 : if (loaded != _isTextureLoaded && loaded) {
165 20813 : onTextureLoaded();
166 20813 : _isTextureLoaded = loaded;
167 : }
168 : }
169 247190 : return DynamicStateNode::visitDraw(frame, parentFlags);
170 : }
171 :
172 142982 : void Sprite::draw(FrameInfo &frame, NodeFlags flags) {
173 142982 : if (!_texture || !_texture->isLoaded()) {
174 10 : return;
175 : }
176 :
177 142972 : if (_autofit != Autofit::None) {
178 23602 : auto size = _texture->getExtent();
179 23602 : if (_targetTextureSize != size) {
180 16168 : _targetTextureSize = size;
181 16168 : _vertexesDirty = true;
182 : }
183 : }
184 :
185 142972 : if (checkVertexDirty()) {
186 36612 : updateVertexes();
187 36612 : _vertexesDirty = false;
188 : }
189 :
190 142972 : if (_vertexColorDirty) {
191 35764 : updateVertexesColor();
192 35764 : _vertexColorDirty = false;
193 : }
194 :
195 142972 : if (_materialDirty) {
196 19658 : updateBlendAndDepth();
197 :
198 19658 : auto info = getMaterialInfo();
199 19658 : _materialId = frame.currentContext->context->getMaterial(info);
200 19658 : if (_materialId == 0) {
201 66 : _materialId = frame.currentContext->context->acquireMaterial(info, getMaterialImages(), nullptr, isMaterialRevokable());
202 66 : if (_materialId == 0) {
203 0 : log::warn("Sprite", "Material for sprite with texture '", _texture->getName(), "' not found");
204 : }
205 : }
206 19658 : _materialDirty = false;
207 : }
208 :
209 143925 : for (auto &it : _pendingDependencies) {
210 953 : emplace_ordered(frame.currentContext->waitDependencies, move(it));
211 : }
212 :
213 142972 : if (_linearGradient) {
214 370 : auto context = frame.contextStack.back();
215 :
216 370 : DrawStateValues state;
217 : // copy current state
218 370 : auto stateId = context->getCurrentState();
219 370 : if (stateId != StateIdNone) {
220 370 : state = *context->getState(stateId);
221 : }
222 :
223 370 : auto newData = Rc<StateData>::create(dynamic_cast<StateData *>(state.data ? state.data.get() : nullptr));
224 370 : auto transform = frame.modelTransformStack.back();
225 370 : transform.scale(_contentSize.width, _contentSize.height, 1.0f);
226 :
227 370 : newData->transform = transform;
228 370 : newData->gradient = _linearGradient->pop();
229 370 : state.data = newData;
230 :
231 370 : auto newStateId = context->addState(state);
232 :
233 370 : context->stateStack.push_back(newStateId);
234 370 : }
235 :
236 142972 : pushCommands(frame, flags);
237 :
238 142972 : if (_linearGradient) {
239 370 : frame.contextStack.back()->stateStack.pop_back();
240 : }
241 :
242 142972 : _pendingDependencies.clear();
243 : }
244 :
245 23444 : void Sprite::onEnter(Scene *scene) {
246 23444 : Node::onEnter(scene);
247 :
248 23444 : if (!_textureName.empty()) {
249 23444 : if (!_texture || _texture->getName() != _textureName) {
250 21979 : if (auto &cache = _director->getResourceCache()) {
251 21979 : _texture = cache->acquireTexture(_textureName);
252 21979 : if (_texture) {
253 21979 : updateBlendAndDepth();
254 : }
255 21979 : _materialDirty = true;
256 : }
257 : }
258 : }
259 :
260 23444 : if (_texture) {
261 23444 : _texture->onEnter(_frameContext);
262 : }
263 23444 : }
264 :
265 23444 : void Sprite::onExit() {
266 23444 : if (_texture) {
267 23444 : _texture->onExit(_frameContext);
268 : }
269 23444 : Node::onExit();
270 23444 : }
271 :
272 27763 : void Sprite::onContentSizeDirty() {
273 27763 : _vertexesDirty = true;
274 27763 : Node::onContentSizeDirty();
275 27763 : }
276 :
277 20813 : void Sprite::onTextureLoaded() {
278 20813 : if (_textureLoadedCallback) {
279 6 : _textureLoadedCallback();
280 : }
281 20813 : }
282 :
283 2890 : void Sprite::setColorMode(const core::ColorMode &mode) {
284 2890 : if (_colorMode != mode) {
285 2890 : _colorMode = mode;
286 2890 : _materialDirty = true;
287 : }
288 2890 : }
289 :
290 12 : void Sprite::setBlendInfo(const core::BlendInfo &info) {
291 12 : if (_blendInfo != info) {
292 12 : _blendInfo = info;
293 12 : _materialInfo.setBlendInfo(info);
294 12 : _materialDirty = true;
295 : }
296 12 : }
297 :
298 12 : void Sprite::setLineWidth(float value) {
299 12 : if (_materialInfo.getLineWidth() != value) {
300 12 : _materialInfo.setLineWidth(value);
301 12 : _materialDirty = true;
302 : }
303 12 : }
304 :
305 7244 : void Sprite::setRenderingLevel(RenderingLevel level) {
306 7244 : if (_renderingLevel != level) {
307 1507 : _renderingLevel = level;
308 1507 : if (_running) {
309 42 : updateBlendAndDepth();
310 : }
311 : }
312 7244 : }
313 :
314 1465 : void Sprite::setNormalized(bool value) {
315 1465 : if (_normalized != value) {
316 1465 : _normalized = value;
317 : }
318 1465 : }
319 :
320 16178 : void Sprite::setAutofit(Autofit autofit) {
321 16178 : if (_autofit != autofit) {
322 16168 : _autofit = autofit;
323 16168 : _vertexesDirty = true;
324 : }
325 16178 : }
326 :
327 6 : void Sprite::setAutofitPosition(const Vec2 &vec) {
328 6 : if (_autofitPos != vec) {
329 6 : _autofitPos = vec;
330 6 : if (_autofit != Autofit::None) {
331 0 : _vertexesDirty = true;
332 : }
333 : }
334 6 : }
335 :
336 10 : void Sprite::setSamplerIndex(uint16_t idx) {
337 10 : if (_samplerIdx != idx) {
338 10 : _samplerIdx = idx;
339 10 : _materialDirty = true;
340 : }
341 10 : }
342 :
343 6 : void Sprite::setTextureLoadedCallback(Function<void()> &&cb) {
344 6 : _textureLoadedCallback = move(cb);
345 6 : }
346 :
347 0 : void Sprite::pushShadowCommands(FrameInfo &frame, NodeFlags flags, const Mat4 &t, SpanView<TransformVertexData> data) {
348 0 : auto p = new (memory::pool::palloc(frame.pool->getPool(), sizeof(TransformVertexData) * data.size())) TransformVertexData();
349 0 : for (auto &it : data) {
350 0 : p->transform = it.transform;
351 0 : p->data = it.data;
352 0 : ++ p;
353 : }
354 :
355 0 : FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
356 0 : handle->shadows->pushShadowArray(makeSpanView(p, data.size()), handle->getCurrentState(), frame.depthStack.back());
357 0 : }
358 :
359 52163 : void Sprite::pushCommands(FrameInfo &frame, NodeFlags flags) {
360 52163 : auto data = _vertexes.pop();
361 52163 : Mat4 newMV;
362 52163 : if (_normalized) {
363 0 : auto &modelTransform = frame.modelTransformStack.back();
364 0 : newMV.m[12] = floorf(modelTransform.m[12]);
365 0 : newMV.m[13] = floorf(modelTransform.m[13]);
366 0 : newMV.m[14] = floorf(modelTransform.m[14]);
367 : } else {
368 52163 : newMV = frame.modelTransformStack.back();
369 : }
370 :
371 52163 : FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
372 :
373 52163 : if (_depthIndex > 0.0f) {
374 0 : TransformVertexData transformData{newMV, data};
375 0 : pushShadowCommands(frame, flags, newMV, makeSpanView(&transformData, 1));
376 0 : }
377 104326 : handle->commands->pushVertexArray(data.get(), frame.viewProjectionStack.back() * newMV,
378 52163 : frame.zPath, _materialId, handle->getCurrentState(), _realRenderingLevel, frame.depthStack.back() * _displayedColor.a, _commandFlags);
379 52163 : }
380 :
381 19658 : MaterialInfo Sprite::getMaterialInfo() const {
382 19658 : MaterialInfo ret;
383 19658 : ret.images[0] = _texture->getIndex();
384 19658 : ret.samplers[0] = _samplerIdx;
385 19658 : ret.colorModes[0] = _colorMode;
386 19658 : ret.pipeline = _materialInfo;
387 19658 : return ret;
388 : }
389 :
390 66 : Vector<core::MaterialImage> Sprite::getMaterialImages() const {
391 66 : Vector<core::MaterialImage> ret;
392 66 : ret.emplace_back(_texture->getMaterialImage());
393 66 : return ret;
394 0 : }
395 :
396 66 : bool Sprite::isMaterialRevokable() const {
397 66 : return _texture && _texture->getTemporary();
398 : }
399 :
400 1657613 : void Sprite::updateColor() {
401 1657613 : if (_tmpColor != _displayedColor) {
402 86884 : _vertexColorDirty = true;
403 86884 : if (_tmpColor.a != _displayedColor.a) {
404 31735 : if (_displayedColor.a == 1.0f || _tmpColor.a == 1.0f) {
405 25309 : updateBlendAndDepth();
406 : }
407 : }
408 86884 : _tmpColor = _displayedColor;
409 : }
410 1657613 : }
411 :
412 185 : void Sprite::updateVertexesColor() {
413 185 : _vertexes.updateColor(_displayedColor);
414 185 : }
415 :
416 5908 : void Sprite::initVertexes() {
417 5908 : _vertexes.init(4, 6);
418 5908 : _vertexesDirty = true;
419 5908 : }
420 :
421 76 : void Sprite::updateVertexes() {
422 76 : _vertexes.clear();
423 :
424 76 : auto texExtent = _texture->getExtent();
425 76 : auto texSize = Size2(texExtent.width, texExtent.height);
426 :
427 76 : texSize = Size2(texSize.width * _textureRect.size.width, texSize.height * _textureRect.size.height);
428 :
429 76 : Rect contentRect;
430 76 : Rect textureRect;
431 :
432 76 : if (!getAutofitParams(_autofit, _autofitPos, _contentSize, Size2(texSize.width, texSize.height), contentRect, textureRect)) {
433 26 : texSize = Size2(1.0f, 1.0f);
434 26 : contentRect = Rect(0.0f, 0.0f, _contentSize.width, _contentSize.height);
435 26 : textureRect = _textureRect;
436 : } else {
437 50 : textureRect = Rect(
438 50 : _textureRect.origin.x + textureRect.origin.x / texSize.width,
439 50 : _textureRect.origin.y + textureRect.origin.y / texSize.height,
440 50 : textureRect.size.width / texSize.width,
441 50 : textureRect.size.height / texSize.height);
442 : }
443 :
444 76 : _vertexes.addQuad()
445 76 : .setGeometry(Vec4(contentRect.origin.x, contentRect.origin.y, 0.0f, 1.0f), contentRect.size)
446 76 : .setTextureRect(textureRect, 1.0f, 1.0f, _flippedX, _flippedY, _rotated)
447 76 : .setColor(_displayedColor);
448 :
449 76 : _vertexColorDirty = false;
450 76 : }
451 :
452 68469 : void Sprite::updateBlendAndDepth() {
453 68469 : bool shouldBlendColors = false;
454 68469 : bool shouldWriteDepth = false;
455 :
456 68469 : _realRenderingLevel = getRealRenderingLevel();
457 68469 : switch (_realRenderingLevel) {
458 0 : case RenderingLevel::Default:
459 0 : break;
460 19396 : case RenderingLevel::Solid:
461 19396 : shouldWriteDepth = true;
462 19396 : shouldBlendColors = false;
463 19396 : break;
464 4244 : case RenderingLevel::Surface:
465 4244 : shouldBlendColors = true;
466 4244 : shouldWriteDepth = false;
467 4244 : break;
468 44829 : case RenderingLevel::Transparent:
469 44829 : shouldBlendColors = true;
470 44829 : shouldWriteDepth = false;
471 44829 : break;
472 : }
473 :
474 68469 : if (shouldBlendColors) {
475 49073 : if (!_blendInfo.enabled) {
476 34 : _blendInfo.enabled = 1;
477 34 : _materialDirty = true;
478 : }
479 : } else {
480 19396 : if (_blendInfo.enabled) {
481 18081 : _blendInfo.enabled = 0;
482 18081 : _materialDirty = true;
483 : }
484 : }
485 :
486 68469 : _materialInfo.setBlendInfo(_blendInfo);
487 :
488 68469 : auto depth = _materialInfo.getDepthInfo();
489 68469 : if (shouldWriteDepth) {
490 19396 : if (!depth.writeEnabled) {
491 18081 : depth.writeEnabled = 1;
492 18081 : _materialDirty = true;
493 : }
494 : } else {
495 49073 : if (depth.writeEnabled) {
496 34 : depth.writeEnabled = 0;
497 34 : _materialDirty = true;
498 : }
499 : }
500 68469 : if (_realRenderingLevel == RenderingLevel::Surface || _realRenderingLevel == RenderingLevel::Transparent) {
501 49073 : if (depth.compare != toInt(core::CompareOp::LessOrEqual)) {
502 23472 : depth.compare = toInt(core::CompareOp::LessOrEqual);
503 23472 : _materialDirty = true;
504 : }
505 : } else {
506 19396 : if (depth.compare != toInt(core::CompareOp::Less)) {
507 18075 : depth.compare = toInt(core::CompareOp::Less);
508 18075 : _materialDirty = true;
509 : }
510 : }
511 68469 : _materialInfo.setDepthInfo(depth);
512 68469 : }
513 :
514 11820 : RenderingLevel Sprite::getRealRenderingLevel() const {
515 11820 : auto level = _renderingLevel;
516 11820 : if (level == RenderingLevel::Default) {
517 9154 : RenderingLevel parentLevel = RenderingLevel::Default;
518 9154 : auto p = _parent;
519 32176 : while (p) {
520 23022 : if (auto s = dynamic_cast<Sprite *>(p)) {
521 12052 : if (s->getRenderingLevel() != RenderingLevel::Default) {
522 8800 : parentLevel = std::max(s->getRenderingLevel(), parentLevel);
523 : }
524 : }
525 23022 : p = p->getParent();
526 : }
527 9154 : if (_displayedColor.a < 1.0f || !_texture || _materialInfo.getLineWidth() != 0.0f) {
528 8970 : level = RenderingLevel::Transparent;
529 184 : } else if (_colorMode.getMode() == core::ColorMode::Solid) {
530 184 : if (_texture->hasAlpha()) {
531 0 : level = RenderingLevel::Transparent;
532 : } else {
533 184 : level = RenderingLevel::Solid;
534 : }
535 : } else {
536 0 : auto alphaMapping = _colorMode.getA();
537 0 : switch (alphaMapping) {
538 0 : case core::ComponentMapping::Identity:
539 0 : if (_texture->hasAlpha()) {
540 0 : level = RenderingLevel::Transparent;
541 : } else {
542 0 : level = RenderingLevel::Solid;
543 : }
544 0 : break;
545 0 : case core::ComponentMapping::Zero:
546 0 : level = RenderingLevel::Transparent;
547 0 : break;
548 0 : case core::ComponentMapping::One:
549 0 : level = RenderingLevel::Solid;
550 0 : break;
551 0 : default:
552 0 : level = RenderingLevel::Transparent;
553 0 : break;
554 : }
555 : }
556 9154 : level = std::max(level, parentLevel);
557 : }
558 11820 : return level;
559 : }
560 :
561 98099 : bool Sprite::checkVertexDirty() const {
562 98099 : return _vertexesDirty;
563 : }
564 :
565 76 : bool Sprite::getAutofitParams(Autofit autofit, const Vec2 &autofitPos, const Size2 &contentSize, const Size2 &texSize,
566 : Rect &contentRect, Rect &textureRect) {
567 :
568 76 : contentRect = Rect(Vec2::ZERO, contentSize);
569 :
570 76 : float scale = 1.0f;
571 76 : switch (autofit) {
572 26 : case Autofit::None:
573 26 : return false;
574 : break;
575 10 : case Autofit::Width: scale = texSize.width / contentSize.width; break;
576 10 : case Autofit::Height: scale = texSize.height / contentSize.height; break;
577 20 : case Autofit::Contain: scale = std::max(texSize.width / contentSize.width, texSize.height / contentSize.height); break;
578 10 : case Autofit::Cover: scale = std::min(texSize.width / contentSize.width, texSize.height / contentSize.height); break;
579 : }
580 :
581 50 : contentRect = Rect(Vec2::ZERO, contentSize);
582 50 : textureRect = Rect(0, 0, texSize.width, texSize.height);
583 :
584 50 : auto texSizeInView = Size2(texSize.width / scale, texSize.height / scale);
585 50 : if (texSizeInView.width < contentSize.width) {
586 20 : contentRect.size.width -= (contentSize.width - texSizeInView.width);
587 20 : contentRect.origin.x = (contentSize.width - texSizeInView.width) * autofitPos.x;
588 30 : } else if (texSizeInView.width > contentSize.width) {
589 0 : textureRect.origin.x = (textureRect.size.width - contentSize.width * scale) * autofitPos.x;
590 0 : textureRect.size.width = contentSize.width * scale;
591 : }
592 :
593 50 : if (texSizeInView.height < contentSize.height) {
594 10 : contentRect.size.height -= (contentSize.height - texSizeInView.height);
595 10 : contentRect.origin.y = (contentSize.height - texSizeInView.height) * autofitPos.y;
596 40 : } else if (texSizeInView.height > contentSize.height) {
597 20 : textureRect.origin.y = (textureRect.size.height - contentSize.height * scale) * autofitPos.y;
598 20 : textureRect.size.height = contentSize.height * scale;
599 : }
600 :
601 50 : return true;
602 : }
603 :
604 : }
|