mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-29 01:25:38 +00:00
Added rudimentary on-demand texture loading to CarouselComponent.
This commit is contained in:
parent
defbbfd13f
commit
dbfab96e03
|
@ -574,22 +574,6 @@ void GamelistBase::populateList(const std::vector<FileData*>& files, FileData* f
|
||||||
CarouselComponent<FileData*>::Entry carouselEntry;
|
CarouselComponent<FileData*>::Entry carouselEntry;
|
||||||
carouselEntry.name = (*it)->getName();
|
carouselEntry.name = (*it)->getName();
|
||||||
carouselEntry.object = *it;
|
carouselEntry.object = *it;
|
||||||
if (carouselItemType == "" || carouselItemType == "marquee")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getMarqueePath();
|
|
||||||
else if (carouselItemType == "cover")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getCoverPath();
|
|
||||||
else if (carouselItemType == "3dbox")
|
|
||||||
carouselEntry.data.itemPath = (*it)->get3DBoxPath();
|
|
||||||
else if (carouselItemType == "screenshot")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getScreenshotPath();
|
|
||||||
else if (carouselItemType == "titlescreen")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getTitleScreenPath();
|
|
||||||
else if (carouselItemType == "backcover")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getBackCoverPath();
|
|
||||||
else if (carouselItemType == "miximage")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getMiximagePath();
|
|
||||||
else if (carouselItemType == "fanart")
|
|
||||||
carouselEntry.data.itemPath = (*it)->getFanArtPath();
|
|
||||||
|
|
||||||
if (carouselDefaultItem != "")
|
if (carouselDefaultItem != "")
|
||||||
carouselEntry.data.defaultItemPath = carouselDefaultItem;
|
carouselEntry.data.defaultItemPath = carouselDefaultItem;
|
||||||
|
|
|
@ -387,6 +387,9 @@ void GamelistView::updateInfoPanel(const CursorState& state)
|
||||||
mPrimary->getSelected() :
|
mPrimary->getSelected() :
|
||||||
nullptr};
|
nullptr};
|
||||||
|
|
||||||
|
if (mCarousel != nullptr)
|
||||||
|
mCarousel->onDemandTextureLoad();
|
||||||
|
|
||||||
// If the game data has already been rendered to the info panel, then skip it this time.
|
// If the game data has already been rendered to the info panel, then skip it this time.
|
||||||
if (file == mLastUpdated)
|
if (file == mLastUpdated)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,16 +16,16 @@
|
||||||
#include "components/primary/PrimaryComponent.h"
|
#include "components/primary/PrimaryComponent.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
|
|
||||||
struct CarouselElement {
|
struct CarouselEntry {
|
||||||
std::shared_ptr<GuiComponent> item;
|
std::shared_ptr<GuiComponent> item;
|
||||||
std::string itemPath;
|
std::string itemPath;
|
||||||
std::string defaultItemPath;
|
std::string defaultItemPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class CarouselComponent : public PrimaryComponent<T>, protected IList<CarouselElement, T>
|
class CarouselComponent : public PrimaryComponent<T>, protected IList<CarouselEntry, T>
|
||||||
{
|
{
|
||||||
using List = IList<CarouselElement, T>;
|
using List = IList<CarouselEntry, T>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using List::mCursor;
|
using List::mCursor;
|
||||||
|
@ -38,7 +38,7 @@ protected:
|
||||||
using GuiComponent::mZIndex;
|
using GuiComponent::mZIndex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Entry = typename IList<CarouselElement, T>::Entry;
|
using Entry = typename IList<CarouselEntry, T>::Entry;
|
||||||
|
|
||||||
enum class CarouselType {
|
enum class CarouselType {
|
||||||
HORIZONTAL, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
HORIZONTAL, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
|
@ -50,8 +50,10 @@ public:
|
||||||
|
|
||||||
CarouselComponent();
|
CarouselComponent();
|
||||||
|
|
||||||
void addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme = nullptr);
|
void addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme);
|
||||||
|
void updateEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme);
|
||||||
Entry& getEntry(int index) { return mEntries.at(index); }
|
Entry& getEntry(int index) { return mEntries.at(index); }
|
||||||
|
void onDemandTextureLoad();
|
||||||
const CarouselType getType() { return mType; }
|
const CarouselType getType() { return mType; }
|
||||||
const std::string& getItemType() { return mItemType; }
|
const std::string& getItemType() { return mItemType; }
|
||||||
void setItemType(std::string itemType) { mItemType = itemType; }
|
void setItemType(std::string itemType) { mItemType = itemType; }
|
||||||
|
@ -87,7 +89,7 @@ private:
|
||||||
{
|
{
|
||||||
List::stopScrolling();
|
List::stopScrolling();
|
||||||
// Only finish the animation if we're in the gamelist view.
|
// Only finish the animation if we're in the gamelist view.
|
||||||
if constexpr (std::is_same_v<T, FileData*>)
|
if (mGamelistView)
|
||||||
GuiComponent::finishAnimation(0);
|
GuiComponent::finishAnimation(0);
|
||||||
}
|
}
|
||||||
const int getScrollingVelocity() override { return List::getScrollingVelocity(); }
|
const int getScrollingVelocity() override { return List::getScrollingVelocity(); }
|
||||||
|
@ -111,6 +113,7 @@ private:
|
||||||
float mEntryCamOffset;
|
float mEntryCamOffset;
|
||||||
int mPreviousScrollVelocity;
|
int mPreviousScrollVelocity;
|
||||||
bool mTriggerJump;
|
bool mTriggerJump;
|
||||||
|
bool mGamelistView;
|
||||||
|
|
||||||
CarouselType mType;
|
CarouselType mType;
|
||||||
std::string mItemType;
|
std::string mItemType;
|
||||||
|
@ -141,7 +144,7 @@ private:
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
CarouselComponent<T>::CarouselComponent()
|
CarouselComponent<T>::CarouselComponent()
|
||||||
: IList<CarouselElement, T> {LIST_SCROLL_STYLE_SLOW,
|
: IList<CarouselEntry, T> {LIST_SCROLL_STYLE_SLOW,
|
||||||
(std::is_same_v<T, SystemData*> ?
|
(std::is_same_v<T, SystemData*> ?
|
||||||
ListLoopType::LIST_ALWAYS_LOOP :
|
ListLoopType::LIST_ALWAYS_LOOP :
|
||||||
ListLoopType::LIST_PAUSE_AT_END_ON_JUMP)}
|
ListLoopType::LIST_PAUSE_AT_END_ON_JUMP)}
|
||||||
|
@ -149,6 +152,7 @@ CarouselComponent<T>::CarouselComponent()
|
||||||
, mEntryCamOffset {0.0f}
|
, mEntryCamOffset {0.0f}
|
||||||
, mPreviousScrollVelocity {0}
|
, mPreviousScrollVelocity {0}
|
||||||
, mTriggerJump {false}
|
, mTriggerJump {false}
|
||||||
|
, mGamelistView {std::is_same_v<T, FileData*> ? true : false}
|
||||||
, mType {CarouselType::HORIZONTAL}
|
, mType {CarouselType::HORIZONTAL}
|
||||||
, mLegacyMode {false}
|
, mLegacyMode {false}
|
||||||
, mFont {Font::get(FONT_SIZE_LARGE)}
|
, mFont {Font::get(FONT_SIZE_LARGE)}
|
||||||
|
@ -180,7 +184,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
|
||||||
bool legacyMode {theme->isLegacyTheme()};
|
bool legacyMode {theme->isLegacyTheme()};
|
||||||
bool dynamic {true};
|
bool dynamic {true};
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, SystemData*>)
|
if (!mGamelistView)
|
||||||
dynamic = false;
|
dynamic = false;
|
||||||
|
|
||||||
if (legacyMode) {
|
if (legacyMode) {
|
||||||
|
@ -240,7 +244,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
|
||||||
}
|
}
|
||||||
if (!legacyMode) {
|
if (!legacyMode) {
|
||||||
text->setLineSpacing(mLineSpacing);
|
text->setLineSpacing(mLineSpacing);
|
||||||
if constexpr (std::is_same_v<T, SystemData*>) {
|
if (!mGamelistView) {
|
||||||
if (mText != "")
|
if (mText != "")
|
||||||
text->setValue(mText);
|
text->setValue(mText);
|
||||||
}
|
}
|
||||||
|
@ -275,6 +279,85 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
|
||||||
List::add(entry);
|
List::add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme)
|
||||||
|
{
|
||||||
|
if (entry.data.itemPath != "") {
|
||||||
|
auto item = std::make_shared<ImageComponent>(false, true);
|
||||||
|
item->setLinearInterpolation(true);
|
||||||
|
item->setImage(entry.data.itemPath);
|
||||||
|
item->setMaxSize(mItemSize * mItemScale);
|
||||||
|
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
||||||
|
item->setRotateByTargetSize(true);
|
||||||
|
entry.data.item = item;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set origin for the items based on their alignment so they line up properly.
|
||||||
|
if (mItemHorizontalAlignment == ALIGN_LEFT)
|
||||||
|
entry.data.item->setOrigin(0.0f, 0.5f);
|
||||||
|
else if (mItemHorizontalAlignment == ALIGN_RIGHT)
|
||||||
|
entry.data.item->setOrigin(1.0f, 0.5f);
|
||||||
|
else
|
||||||
|
entry.data.item->setOrigin(0.5f, 0.5f);
|
||||||
|
|
||||||
|
if (mItemVerticalAlignment == ALIGN_TOP)
|
||||||
|
entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.0f);
|
||||||
|
else if (mItemVerticalAlignment == ALIGN_BOTTOM)
|
||||||
|
entry.data.item->setOrigin(entry.data.item->getOrigin().x, 1.0f);
|
||||||
|
else
|
||||||
|
entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.5f);
|
||||||
|
|
||||||
|
glm::vec2 denormalized {mItemSize * entry.data.item->getOrigin()};
|
||||||
|
entry.data.item->setPosition(glm::vec3 {denormalized.x, denormalized.y, 0.0f});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void CarouselComponent<T>::onDemandTextureLoad()
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, FileData*>) {
|
||||||
|
int numEntries {static_cast<int>(mEntries.size())};
|
||||||
|
int center {getCursor()};
|
||||||
|
int itemInclusion {static_cast<int>(std::ceil((mMaxItemCount + 1) / 2.0f))};
|
||||||
|
|
||||||
|
for (int i = center - itemInclusion + 1; i < center + itemInclusion; ++i) {
|
||||||
|
int cursor {i};
|
||||||
|
|
||||||
|
while (cursor < 0)
|
||||||
|
cursor += numEntries;
|
||||||
|
while (cursor >= numEntries)
|
||||||
|
cursor -= numEntries;
|
||||||
|
|
||||||
|
auto& entry = mEntries.at(cursor);
|
||||||
|
|
||||||
|
if (entry.data.itemPath == "") {
|
||||||
|
FileData* game {entry.object};
|
||||||
|
|
||||||
|
if (mItemType == "" || mItemType == "marquee")
|
||||||
|
entry.data.itemPath = game->getMarqueePath();
|
||||||
|
else if (mItemType == "cover")
|
||||||
|
entry.data.itemPath = game->getCoverPath();
|
||||||
|
else if (mItemType == "3dbox")
|
||||||
|
entry.data.itemPath = game->get3DBoxPath();
|
||||||
|
else if (mItemType == "screenshot")
|
||||||
|
entry.data.itemPath = game->getScreenshotPath();
|
||||||
|
else if (mItemType == "titlescreen")
|
||||||
|
entry.data.itemPath = game->getTitleScreenPath();
|
||||||
|
else if (mItemType == "backcover")
|
||||||
|
entry.data.itemPath = game->getBackCoverPath();
|
||||||
|
else if (mItemType == "miximage")
|
||||||
|
entry.data.itemPath = game->getMiximagePath();
|
||||||
|
else if (mItemType == "fanart")
|
||||||
|
entry.data.itemPath = game->getFanArtPath();
|
||||||
|
|
||||||
|
auto theme = game->getSystem()->getTheme();
|
||||||
|
updateEntry(entry, theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Input input)
|
template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if (input.value != 0) {
|
if (input.value != 0) {
|
||||||
|
@ -311,7 +394,7 @@ template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Inpu
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, FileData*>) {
|
if (mGamelistView) {
|
||||||
if (config->isMappedLike("leftshoulder", input)) {
|
if (config->isMappedLike("leftshoulder", input)) {
|
||||||
if (mCancelTransitionsCallback)
|
if (mCancelTransitionsCallback)
|
||||||
mCancelTransitionsCallback();
|
mCancelTransitionsCallback();
|
||||||
|
@ -355,7 +438,7 @@ template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Inpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if constexpr (std::is_same_v<T, FileData*>) {
|
if (mGamelistView) {
|
||||||
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
||||||
config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
|
config->isMappedLike("left", input) || config->isMappedLike("right", input) ||
|
||||||
config->isMappedLike("leftshoulder", input) ||
|
config->isMappedLike("leftshoulder", input) ||
|
||||||
|
@ -367,7 +450,7 @@ template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Inpu
|
||||||
mTriggerJump = false;
|
mTriggerJump = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, SystemData*>) {
|
else {
|
||||||
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
||||||
config->isMappedLike("left", input) || config->isMappedLike("right", input))
|
config->isMappedLike("left", input) || config->isMappedLike("right", input))
|
||||||
List::listInput(0);
|
List::listInput(0);
|
||||||
|
@ -385,7 +468,9 @@ template <typename T> void CarouselComponent<T>::update(int deltaTime)
|
||||||
|
|
||||||
template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentTrans)
|
template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentTrans)
|
||||||
{
|
{
|
||||||
if (mEntries.size() == 0)
|
int numEntries {static_cast<int>(mEntries.size())};
|
||||||
|
|
||||||
|
if (numEntries == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
glm::mat4 carouselTrans {parentTrans};
|
glm::mat4 carouselTrans {parentTrans};
|
||||||
|
@ -475,15 +560,26 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
|
|
||||||
int center {static_cast<int>(mEntryCamOffset)};
|
int center {static_cast<int>(mEntryCamOffset)};
|
||||||
int itemInclusion {static_cast<int>(std::ceil(mMaxItemCount / 2.0f))};
|
int itemInclusion {static_cast<int>(std::ceil(mMaxItemCount / 2.0f))};
|
||||||
bool singleEntry {mEntries.size() == 1};
|
bool singleEntry {numEntries == 1};
|
||||||
|
|
||||||
|
struct renderStruct {
|
||||||
|
int index;
|
||||||
|
float distance;
|
||||||
|
float scale;
|
||||||
|
float opacity;
|
||||||
|
glm::mat4 trans;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<renderStruct> renderItems;
|
||||||
|
std::vector<renderStruct> renderItemsSorted;
|
||||||
|
|
||||||
for (int i = center - itemInclusion; i < center + itemInclusion + 2; ++i) {
|
for (int i = center - itemInclusion; i < center + itemInclusion + 2; ++i) {
|
||||||
int index {i};
|
int index {i};
|
||||||
|
|
||||||
while (index < 0)
|
while (index < 0)
|
||||||
index += static_cast<int>(mEntries.size());
|
index += numEntries;
|
||||||
while (index >= static_cast<int>(mEntries.size()))
|
while (index >= numEntries)
|
||||||
index -= static_cast<int>(mEntries.size());
|
index -= numEntries;
|
||||||
|
|
||||||
float distance {i - mEntryCamOffset};
|
float distance {i - mEntryCamOffset};
|
||||||
|
|
||||||
|
@ -514,25 +610,47 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
opacity = mUnfocusedItemOpacity + (maxDiff - (maxDiff * fabsf(distance)));
|
opacity = mUnfocusedItemOpacity + (maxDiff - (maxDiff * fabsf(distance)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<GuiComponent>& comp {mEntries.at(index).data.item};
|
renderStruct renderItem;
|
||||||
|
renderItem.index = index;
|
||||||
|
renderItem.distance = distance;
|
||||||
|
renderItem.scale = scale;
|
||||||
|
renderItem.opacity = opacity;
|
||||||
|
renderItem.trans = itemTrans;
|
||||||
|
|
||||||
|
renderItems.emplace_back(renderItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int belowCenter {static_cast<int>(std::ceil(renderItems.size() / 2)) - 1};
|
||||||
|
|
||||||
|
// TODO: Fix glitches when navigating to the right.
|
||||||
|
// The following sorting makes sure that overlapping items are rendered in the correct order.
|
||||||
|
for (int i = 0; i < belowCenter - 0; ++i)
|
||||||
|
renderItemsSorted.emplace_back(renderItems[i]);
|
||||||
|
|
||||||
|
for (int i = static_cast<int>(renderItems.size()) - 1; i > belowCenter - 1; --i)
|
||||||
|
renderItemsSorted.emplace_back(renderItems[i]);
|
||||||
|
|
||||||
|
for (auto& renderItem : renderItemsSorted) {
|
||||||
|
const std::shared_ptr<GuiComponent>& comp {mEntries.at(renderItem.index).data.item};
|
||||||
|
|
||||||
if (comp == nullptr)
|
if (comp == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mType == CarouselType::VERTICAL_WHEEL || mType == CarouselType::HORIZONTAL_WHEEL) {
|
if (mType == CarouselType::VERTICAL_WHEEL || mType == CarouselType::HORIZONTAL_WHEEL) {
|
||||||
comp->setRotationDegrees(mItemRotation * distance);
|
comp->setRotationDegrees(mItemRotation * renderItem.distance);
|
||||||
comp->setRotationOrigin(mItemRotationOrigin);
|
comp->setRotationOrigin(mItemRotationOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
comp->setScale(scale);
|
comp->setScale(renderItem.scale);
|
||||||
comp->setOpacity(opacity);
|
comp->setOpacity(renderItem.opacity);
|
||||||
comp->render(itemTrans);
|
comp->render(renderItem.trans);
|
||||||
|
|
||||||
|
// TODO: Rewrite to use "real" reflections instead of this hack.
|
||||||
// Don't attempt to add reflections for text entries.
|
// Don't attempt to add reflections for text entries.
|
||||||
if (mReflections && (mEntries.at(index).data.itemPath != "" ||
|
if (mReflections && (mEntries.at(renderItem.index).data.itemPath != "" ||
|
||||||
mEntries.at(index).data.defaultItemPath != "")) {
|
mEntries.at(renderItem.index).data.defaultItemPath != "")) {
|
||||||
itemTrans =
|
glm::mat4 reflectionTrans {glm::translate(
|
||||||
glm::translate(itemTrans, glm::vec3 {0.0f, comp->getSize().y * scale, 0.0f});
|
renderItem.trans, glm::vec3 {0.0f, comp->getSize().y * renderItem.scale, 0.0f})};
|
||||||
const unsigned int colorShift {comp->getColorShift()};
|
const unsigned int colorShift {comp->getColorShift()};
|
||||||
comp->setColorGradientHorizontal(false);
|
comp->setColorGradientHorizontal(false);
|
||||||
comp->setColorShift(0xFFFFFF00 | static_cast<int>(mReflectionsOpacity * 255.0f));
|
comp->setColorShift(0xFFFFFF00 | static_cast<int>(mReflectionsOpacity * 255.0f));
|
||||||
|
@ -543,7 +661,7 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
if (mReflectionsFalloff > 1.0f)
|
if (mReflectionsFalloff > 1.0f)
|
||||||
comp->setReflectionsFalloff(mReflectionsFalloff - 1.0f);
|
comp->setReflectionsFalloff(mReflectionsFalloff - 1.0f);
|
||||||
comp->setFlipY(true);
|
comp->setFlipY(true);
|
||||||
comp->render(itemTrans);
|
comp->render(reflectionTrans);
|
||||||
comp->setFlipY(false);
|
comp->setFlipY(false);
|
||||||
comp->setColorShift(colorShift);
|
comp->setColorShift(colorShift);
|
||||||
comp->setReflectionsFalloff(0.0f);
|
comp->setReflectionsFalloff(0.0f);
|
||||||
|
|
Loading…
Reference in a new issue