From 03387206020f0a7bb82d146800fa2812ef8d8cf9 Mon Sep 17 00:00:00 2001 From: fallahn Date: Sun, 23 Apr 2023 16:04:58 +0100 Subject: [PATCH 01/20] tweak green shader --- libsocial/include/Social.hpp | 2 +- samples/golf/src/golf/TerrainShader.inl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsocial/include/Social.hpp b/libsocial/include/Social.hpp index 99bec377a..d9fd80336 100644 --- a/libsocial/include/Social.hpp +++ b/libsocial/include/Social.hpp @@ -45,7 +45,7 @@ source distribution. //(terrain vertex data and materials changed 1100 -> 1110) //(player avatar data format changed 1110 -> 1120) static constexpr std::uint16_t CURRENT_VER = 1120; -static const std::string StringVer("1.12.1"); +static const std::string StringVer("1.12.2"); class Social final diff --git a/samples/golf/src/golf/TerrainShader.inl b/samples/golf/src/golf/TerrainShader.inl index 411557fdb..4e422f5a0 100644 --- a/samples/golf/src/golf/TerrainShader.inl +++ b/samples/golf/src/golf/TerrainShader.inl @@ -183,7 +183,7 @@ R"( vec4 colour = vec4(c, v_heightData.x); colour.a *= u_alpha; - colour = mix(vec4(DotColour, /*smoothstep(0.05, 0.15, v_heightData.x * u_alpha) * 0.98*/0.5 + (0.5 * u_alpha)), colour, alpha); + colour = mix(vec4(DotColour, (0.5 + (0.5 * u_alpha)) * v_heightData.x), colour, alpha); FRAG_OUT = colour; } From a43a6c10bd369773fd55098eb0c2481867f11fad Mon Sep 17 00:00:00 2001 From: fallahn Date: Mon, 24 Apr 2023 11:29:34 +0100 Subject: [PATCH 02/20] fix typo in Rectagle::combine() increase AFK timeout use label text area for keybind buttons --- crogine/include/crogine/util/Rectangle.hpp | 2 +- samples/golf/src/golf/GolfState.cpp | 2 +- samples/golf/src/golf/OptionsState.cpp | 29 +++++++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/crogine/include/crogine/util/Rectangle.hpp b/crogine/include/crogine/util/Rectangle.hpp index deae877a0..008c3623c 100644 --- a/crogine/include/crogine/util/Rectangle.hpp +++ b/crogine/include/crogine/util/Rectangle.hpp @@ -91,7 +91,7 @@ namespace cro cro::Rectangle ret(std::min(a.left, b.left), std::min(a.bottom, b.bottom), std::max(a.left + a.width, b.left + b.width), - std::max(a.botom + a.height, b.bottom + b.height)); + std::max(a.bottom + a.height, b.bottom + b.height)); ret.width -= ret.left; ret.height -= ret.bottom; diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 1554071c2..4572f88f0 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -148,7 +148,7 @@ namespace float godmode = 1.f; bool allowAchievements = false; - const cro::Time DefaultIdleTime = cro::seconds(90.f); + const cro::Time DefaultIdleTime = cro::seconds(180.f); cro::Time idleTime = DefaultIdleTime; constexpr std::uint32_t MaxCascades = 4; //actual value is 1 less this - see ShadowQuality::update() diff --git a/samples/golf/src/golf/OptionsState.cpp b/samples/golf/src/golf/OptionsState.cpp index 1fa50324e..e33b26f0c 100644 --- a/samples/golf/src/golf/OptionsState.cpp +++ b/samples/golf/src/golf/OptionsState.cpp @@ -69,6 +69,7 @@ source distribution. #include #include +#include #include #include @@ -148,6 +149,9 @@ namespace 0,2,4,8 }; + //this needs to be here to be accessed by label callback + std::array bindingEnts = {}; //buttons to activate key rebinding + struct SliderData final { const glm::vec2 basePosition = glm::vec2(0.f); @@ -2121,11 +2125,11 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& auto infoEnt = m_scene.createEntity(); infoEnt.addComponent().setPosition(glm::vec3(parentBounds.width / 2.f, parentBounds.height - 8.f, TextOffset)); infoEnt.addComponent(); - infoEnt.addComponent(uiFont);// .setString("Info Text"); + infoEnt.addComponent(uiFont).setString("Click On A Keybind To Change It"); infoEnt.getComponent().setFillColour(TextNormalColour); infoEnt.getComponent().setCharacterSize(UITextSize); infoEnt.addComponent().ID = CommandID::Menu::PlayerConfig; //not the best description, just recycling existing members here... - + centreText(infoEnt); infoEnt.addComponent().function = [](cro::Entity e, float dt) { @@ -2155,7 +2159,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& auto unselectID = uiSystem.addCallback( [infoEnt, buttonChangeEnt](cro::Entity e) mutable { - infoEnt.getComponent().setString(" "); + infoEnt.getComponent().setString("Click On A Keybind To Change It"); centreText(infoEnt); buttonChangeEnt.getComponent().setFillColour(cro::Colour::Transparent); e.getComponent().setColour(cro::Colour::Transparent); @@ -2196,6 +2200,11 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& }); parent.getComponent().addChild(entity.getComponent()); + + if (keyIndex > -1) + { + bindingEnts[keyIndex] = entity; + } return entity; }; @@ -2501,6 +2510,20 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& e.getComponent().setString(str); centreText(e); + + //update the button area to include the text + auto pos = e.getComponent().getPosition(); + auto area = cro::Text::getLocalBounds(e); + area.left += pos.x; + area.bottom += pos.y; + + pos = bindingEnts[labelIndex].getComponent().getPosition(); + area.left -= pos.x; + area.bottom -= pos.y; + + //auto buttonArea = bindingEnts[labelIndex].getComponent().getLocalBounds(); + bindingEnts[labelIndex].getComponent().area = area;// cro::Util::Rectangle::combine(area, buttonArea); + bindingEnts[labelIndex].getComponent().setRotation(0.f);//hack to trigger transform callback } previousKey = key; } From d8c065683f267ac8262849258cb76c879c9fdf0c Mon Sep 17 00:00:00 2001 From: fallahn Date: Mon, 24 Apr 2023 11:44:20 +0100 Subject: [PATCH 03/20] add notification on new avatar download --- libsocial/include/Social.hpp | 1 + samples/golf/src/golf/MenuState.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/libsocial/include/Social.hpp b/libsocial/include/Social.hpp index d9fd80336..6e7558355 100644 --- a/libsocial/include/Social.hpp +++ b/libsocial/include/Social.hpp @@ -74,6 +74,7 @@ class Social final { enum { + AvatarDownloaded, LevelUp, XPAwarded, OverlayActivated, diff --git a/samples/golf/src/golf/MenuState.cpp b/samples/golf/src/golf/MenuState.cpp index 64284b39a..e87336dfd 100644 --- a/samples/golf/src/golf/MenuState.cpp +++ b/samples/golf/src/golf/MenuState.cpp @@ -797,6 +797,18 @@ void MenuState::handleMessage(const cro::Message& msg) const auto& data = msg.getData(); ugcInstalledHandler(data.itemID, data.type); } + else if (msg.id == Social::MessageID::SocialMessage) + { + const auto& data = msg.getData(); + if (data.type == Social::SocialEvent::AvatarDownloaded + && m_currentMenu == MenuID::Lobby) + { + //TODO we've only updated a specific avatar + //so it would be preferable to not update ALL + //the textures each time. + updateLobbyAvatars(); + } + } #endif m_backgroundScene.forwardMessage(msg); From 1cf3e5cd81d396068f212cc0f5bf82c8e683ac32 Mon Sep 17 00:00:00 2001 From: fallahn Date: Mon, 24 Apr 2023 12:28:02 +0100 Subject: [PATCH 04/20] don't render drone on minim-map add avatar icon to active player name --- samples/golf/src/golf/GolfState.cpp | 8 +++++- samples/golf/src/golf/GolfStateUI.cpp | 40 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 4572f88f0..c1723148a 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -4574,6 +4574,8 @@ void GolfState::createDrone() material.blendMode = cro::Material::BlendMode::Alpha; entity.getComponent().setMaterial(1, material); + entity.getComponent().setRenderFlags(~(RenderFlags::MiniGreen | RenderFlags::MiniMap)); + entity.addComponent(); cro::AudioScape as; @@ -6148,6 +6150,7 @@ void GolfState::setCurrentPlayer(const ActivePlayer& player) auto& data = e.getComponent().getUserData(); data.string = m_sharedData.connectionData[m_currentPlayer.client].playerData[m_currentPlayer.player].name; e.getComponent().active = true; + e.getComponent().setScale(glm::vec2(1.f)); }; m_uiScene.getSystem()->sendCommand(cmd); @@ -6888,7 +6891,10 @@ void GolfState::createTransition(const ActivePlayer& playerData) cmd.action = [&](cro::Entity e, float) { - e.getComponent().setString(" "); + e.getComponent().setScale(glm::vec2(0.f)); //also hides attached icon + auto& data = e.getComponent().getUserData(); + data.string = " "; + e.getComponent().active = true; }; m_uiScene.getSystem()->sendCommand(cmd); diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index 1af5304ef..761061c4b 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -237,7 +237,47 @@ void GolfState::buildUI() auto nameEnt = entity; infoEnt.getComponent().addChild(entity.getComponent()); + //player avatar + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ -20.f, -10.f }); + entity.getComponent().setScale(glm::vec2(0.f)); + entity.addComponent(); + entity.addComponent(m_sharedData.nameTextures[0].getTexture()); + entity.addComponent().active = true; + entity.getComponent().setUserData(1); + entity.getComponent().function = + [&, nameEnt](cro::Entity e, float) + { + static constexpr auto BaseScale = glm::vec2(16.f) / LabelIconSize; + if (nameEnt.getComponent().active) + { + //set scale based on progress + const auto& data = nameEnt.getComponent().getUserData(); + float scale = static_cast(data.currentChar) / data.string.size(); + + //TODO could probably use the timer to interpolate scale + e.getComponent().setScale(BaseScale * glm::vec2(scale, 1.f)); + + //if the scale is zero set the new texture properties + auto& prevChar = e.getComponent().getUserData(); + if (data.currentChar == 0 + && prevChar != 0) + { + e.getComponent().setTexture(m_sharedData.nameTextures[m_currentPlayer.client].getTexture()); + cro::FloatRect bounds = { 0.f, LabelTextureSize.y - (LabelIconSize.x * 4.f), LabelIconSize.x, LabelIconSize.y }; + bounds.left = LabelIconSize.x * (m_currentPlayer.player % 2); + bounds.bottom += LabelIconSize.y * (m_currentPlayer.player / 2); + e.getComponent().setTextureRect(bounds); + } + prevChar = data.currentChar; + } + else + { + + } + }; + nameEnt.getComponent().addChild(entity.getComponent()); //think bulb displayed when CPU players are thinking entity = m_uiScene.createEntity(); From f5e839974e325d7c4336803c92a231feab701bbe Mon Sep 17 00:00:00 2001 From: fallahn Date: Mon, 24 Apr 2023 14:03:00 +0100 Subject: [PATCH 05/20] add avatar icon to scoreboard --- samples/golf/src/golf/GameConsts.hpp | 8 ++++++ samples/golf/src/golf/GolfState.cpp | 14 ++++++++- samples/golf/src/golf/GolfStateUI.cpp | 41 +++++++++++++++++++++------ 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/samples/golf/src/golf/GameConsts.hpp b/samples/golf/src/golf/GameConsts.hpp index d84aebb86..e3d723e2c 100644 --- a/samples/golf/src/golf/GameConsts.hpp +++ b/samples/golf/src/golf/GameConsts.hpp @@ -329,6 +329,14 @@ static inline constexpr float smoothstep(float edge0, float edge1, float x) return t * t * (3.f - 2.f * t); } +static inline cro::FloatRect getAvatarBounds(std::uint8_t player) +{ + cro::FloatRect bounds = { 0.f, LabelTextureSize.y - (LabelIconSize.x * 4.f), LabelIconSize.x, LabelIconSize.y }; + bounds.left = LabelIconSize.x * (player % 2); + bounds.bottom += LabelIconSize.y * (player / 2); + return bounds; +} + static inline cro::Image cropAvatarImage(const std::string& path) { cro::ImageArray arr; diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index c1723148a..744de8e20 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -3390,6 +3390,7 @@ void GolfState::addSystems() #ifdef CRO_DEBUG_ m_gameScene.setSystemActive(false); m_gameScene.setSystemActive(false); + //m_gameScene.setSystemActive(false); //can't do this because we rely on player animation events #endif m_gameScene.addDirector(m_resources.textures, m_sharedData); @@ -5841,9 +5842,20 @@ void GolfState::setCurrentHole(std::uint16_t holeInfo) //randomise the cart positions a bit cmd.targetFlags = CommandID::Cart; - cmd.action = [](cro::Entity e, float dt) + cmd.action = [&](cro::Entity e, float dt) { e.getComponent().rotate(cro::Transform::Y_AXIS, dt * 0.5f); + + //move to ground level + auto pos = e.getComponent().getWorldPosition(); + auto result = m_collisionMesh.getTerrain(pos); + float diff = result.height - pos.y; + + e.getComponent().move({ 0.f, diff, 0.f }); + + //and orientate to slope + //auto orientation = lookRotation(pos, pos + result.normal, cro::Transform::Z_AXIS); + //e.getComponent().setRotation(orientation); }; m_gameScene.getSystem()->sendCommand(cmd); } diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index 761061c4b..d590b7c6b 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -239,7 +239,7 @@ void GolfState::buildUI() //player avatar entity = m_uiScene.createEntity(); - entity.addComponent().setPosition({ -20.f, -10.f }); + entity.addComponent().setPosition({ -21.f, -11.f }); entity.getComponent().setScale(glm::vec2(0.f)); entity.addComponent(); entity.addComponent(m_sharedData.nameTextures[0].getTexture()); @@ -248,7 +248,7 @@ void GolfState::buildUI() entity.getComponent().function = [&, nameEnt](cro::Entity e, float) { - static constexpr auto BaseScale = glm::vec2(16.f) / LabelIconSize; + static constexpr auto BaseScale = glm::vec2(14.f) / LabelIconSize; if (nameEnt.getComponent().active) { //set scale based on progress @@ -265,9 +265,7 @@ void GolfState::buildUI() { e.getComponent().setTexture(m_sharedData.nameTextures[m_currentPlayer.client].getTexture()); - cro::FloatRect bounds = { 0.f, LabelTextureSize.y - (LabelIconSize.x * 4.f), LabelIconSize.x, LabelIconSize.y }; - bounds.left = LabelIconSize.x * (m_currentPlayer.player % 2); - bounds.bottom += LabelIconSize.y * (m_currentPlayer.player / 2); + cro::FloatRect bounds = getAvatarBounds(m_currentPlayer.player); e.getComponent().setTextureRect(bounds); } prevChar = data.currentChar; @@ -1424,9 +1422,7 @@ void GolfState::showCountdown(std::uint8_t seconds) m_trophies[i].label.getComponent().setTextureRect(bounds); //choose the relevant player from the sheet - bounds = { 0.f, LabelTextureSize.y - (LabelIconSize.x * 4.f), LabelIconSize.x, LabelIconSize.y }; - bounds.left = LabelIconSize.x * (m_statBoardScores[i].player % 2); - bounds.bottom += LabelIconSize.y * (m_statBoardScores[i].player / 2); + bounds = getAvatarBounds(m_statBoardScores[i].player); m_trophies[i].avatar.getComponent().setTextureRect(bounds); } @@ -1912,6 +1908,35 @@ void GolfState::createScoreboard() bgEnt.getComponent().addChild(entity.getComponent()); m_netStrengthIcons.push_back(entity); + auto barEnt = entity; + + + cro::FloatRect bounds = getAvatarBounds(j); + + //player avatar icon + entity = m_uiScene.createEntity(); + entity.addComponent().setScale(glm::vec2(14.f) / LabelIconSize); + entity.addComponent(); + entity.addComponent(m_sharedData.nameTextures[c.connectionID].getTexture()); + entity.getComponent().setTextureRect(bounds); + entity.addComponent().active = true; + entity.getComponent().function = + [&, barEnt](cro::Entity e, float) + { + if (barEnt.destroyed()) + { + e.getComponent().active = false; + m_uiScene.destroyEntity(e); + } + + //these are set on the ent by updating the scoreboard, rather than rearranging entity positions + auto [client, player] = barEnt.getComponent().getUserData>(); + e.getComponent().setTexture(m_sharedData.nameTextures[client].getTexture(), false); + e.getComponent().setTextureRect(getAvatarBounds(player)); + e.getComponent().setPosition({ 366.f + (scoreboardExpansion * 2.f), 2.f }); + }; + barEnt.getComponent().addChild(entity.getComponent()); + iconPos.y -= IconSpacing; } } From 81828b851673e00a6a0d4bc7e62c9cb3d87a31a0 Mon Sep 17 00:00:00 2001 From: fallahn Date: Mon, 24 Apr 2023 14:24:31 +0100 Subject: [PATCH 06/20] orient golf carts to ground normal --- samples/golf/src/golf/GolfState.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 744de8e20..9b84ed1e5 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -116,6 +116,7 @@ source distribution. #include #include #include +#include #include "../ErrorCheck.hpp" #include @@ -3921,7 +3922,7 @@ void GolfState::buildScene() }; //add a cart for each connected client :3 - for (auto i = 0u; i < m_sharedData.connectionData.size(); ++i) + for (auto i = 0u; i < /*m_sharedData.connectionData.size()*/4u; ++i) { if (m_sharedData.connectionData[i].playerCount > 0) { @@ -5842,10 +5843,8 @@ void GolfState::setCurrentHole(std::uint16_t holeInfo) //randomise the cart positions a bit cmd.targetFlags = CommandID::Cart; - cmd.action = [&](cro::Entity e, float dt) + cmd.action = [&](cro::Entity e, float) { - e.getComponent().rotate(cro::Transform::Y_AXIS, dt * 0.5f); - //move to ground level auto pos = e.getComponent().getWorldPosition(); auto result = m_collisionMesh.getTerrain(pos); @@ -5854,8 +5853,7 @@ void GolfState::setCurrentHole(std::uint16_t holeInfo) e.getComponent().move({ 0.f, diff, 0.f }); //and orientate to slope - //auto orientation = lookRotation(pos, pos + result.normal, cro::Transform::Z_AXIS); - //e.getComponent().setRotation(orientation); + e.getComponent().setRotation(glm::rotation(cro::Transform::Y_AXIS, result.normal)); }; m_gameScene.getSystem()->sendCommand(cmd); } From c337d9062644b9d99f8c76534d4e098e155607d5 Mon Sep 17 00:00:00 2001 From: fallahn Date: Tue, 25 Apr 2023 12:36:32 +0100 Subject: [PATCH 07/20] add getter for ModelDefinition Skeleton components --- crogine/include/crogine/graphics/ModelDefinition.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crogine/include/crogine/graphics/ModelDefinition.hpp b/crogine/include/crogine/graphics/ModelDefinition.hpp index ece59bc0e..3b3c06297 100644 --- a/crogine/include/crogine/graphics/ModelDefinition.hpp +++ b/crogine/include/crogine/graphics/ModelDefinition.hpp @@ -169,6 +169,13 @@ namespace cro */ bool hasSkeleton() const { return m_skeleton; } + /*! + \brief Return a copy of the Skeleton component + Check if the component is valid (models may not always load a skeleton) + with hasSkeleton() + */ + const Skeleton& getSkeleton() const { return m_skeleton; } + /*! \brief Returns the assigned material at the given index, if it exists else returns nullptr From db3677eb83941d6e4fc6c7f2cb98d139775ef3fb Mon Sep 17 00:00:00 2001 From: fallahn Date: Tue, 25 Apr 2023 15:09:47 +0100 Subject: [PATCH 08/20] swap avatar icon and net strength icon positions on score board --- samples/golf/src/golf/GolfStateUI.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index d590b7c6b..f40f8fb4c 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -1889,9 +1889,10 @@ void GolfState::createScoreboard() entity.addComponent(); entity.addComponent() = spriteSheet.getSprite("strength_meter"); entity.addComponent(); + //this is set active only when scoreboard is visible entity.addComponent().setUserData>(c.connectionID, j); entity.getComponent().function = - [&, bgEnt](cro::Entity e, float) + [&, bgEnt, iconPos](cro::Entity e, float) { auto [client, player] = e.getComponent().getUserData>(); @@ -1904,21 +1905,22 @@ void GolfState::createScoreboard() auto idx = m_sharedData.connectionData[client].pingTime / 30; e.getComponent().play(std::min(4u, idx)); } + + auto pos = iconPos; + pos.x += 366.f + (scoreboardExpansion * 2.f); + e.getComponent().setPosition(pos); }; bgEnt.getComponent().addChild(entity.getComponent()); m_netStrengthIcons.push_back(entity); auto barEnt = entity; - - cro::FloatRect bounds = getAvatarBounds(j); - //player avatar icon entity = m_uiScene.createEntity(); entity.addComponent().setScale(glm::vec2(14.f) / LabelIconSize); entity.addComponent(); entity.addComponent(m_sharedData.nameTextures[c.connectionID].getTexture()); - entity.getComponent().setTextureRect(bounds); + entity.getComponent().setTextureRect(getAvatarBounds(j)); entity.addComponent().active = true; entity.getComponent().function = [&, barEnt](cro::Entity e, float) @@ -1933,7 +1935,7 @@ void GolfState::createScoreboard() auto [client, player] = barEnt.getComponent().getUserData>(); e.getComponent().setTexture(m_sharedData.nameTextures[client].getTexture(), false); e.getComponent().setTextureRect(getAvatarBounds(player)); - e.getComponent().setPosition({ 366.f + (scoreboardExpansion * 2.f), 2.f }); + e.getComponent().setPosition({ -(367.f + (scoreboardExpansion * 2.f)), 2.f }); }; barEnt.getComponent().addChild(entity.getComponent()); From 0eb336b23e5061a490655edcbab93e4def8b757b Mon Sep 17 00:00:00 2001 From: fallahn Date: Tue, 25 Apr 2023 15:17:38 +0100 Subject: [PATCH 09/20] fix hitbox alignment on keybind menu --- samples/golf/src/golf/OptionsState.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/samples/golf/src/golf/OptionsState.cpp b/samples/golf/src/golf/OptionsState.cpp index e33b26f0c..eb44fc3b8 100644 --- a/samples/golf/src/golf/OptionsState.cpp +++ b/samples/golf/src/golf/OptionsState.cpp @@ -2519,10 +2519,15 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& pos = bindingEnts[labelIndex].getComponent().getPosition(); area.left -= pos.x; + area.left -= (area.width / 2.f); //compensation for horizontal centering area.bottom -= pos.y; - //auto buttonArea = bindingEnts[labelIndex].getComponent().getLocalBounds(); - bindingEnts[labelIndex].getComponent().area = area;// cro::Util::Rectangle::combine(area, buttonArea); + area.left -= 2.f; + area.width += 4.f; + area.bottom -= 2.f; + area.height += 4.f; + + bindingEnts[labelIndex].getComponent().area = area; bindingEnts[labelIndex].getComponent().setRotation(0.f);//hack to trigger transform callback } previousKey = key; From 01d1fb7dcef3086221837bef3fb0f345eb607296 Mon Sep 17 00:00:00 2001 From: fallahn Date: Wed, 26 Apr 2023 11:33:09 +0100 Subject: [PATCH 10/20] add content path for user avatars --- libsocial/include/Social.hpp | 2 +- libsocial/src/Social.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libsocial/include/Social.hpp b/libsocial/include/Social.hpp index 6e7558355..c1e3e99ce 100644 --- a/libsocial/include/Social.hpp +++ b/libsocial/include/Social.hpp @@ -135,7 +135,7 @@ class Social final enum { Ball, Hair, Course, - Profile + Profile, Avatar }; }; static std::string getBaseContentPath(); diff --git a/libsocial/src/Social.cpp b/libsocial/src/Social.cpp index dd885ba16..83f39fc05 100644 --- a/libsocial/src/Social.cpp +++ b/libsocial/src/Social.cpp @@ -411,5 +411,7 @@ std::string Social::getUserContentPath(std::int32_t contentType) return getBaseContentPath() + "hair/"; case Social::UserContent::Profile: return getBaseContentPath() + "profiles/"; + case Social::UserContent::Avatar: + return getBaseContentPath() + "avatars/"; } } \ No newline at end of file From 23bf9b86d06e21cc5fad4d615bf74272ca7fc1c4 Mon Sep 17 00:00:00 2001 From: fallahn Date: Wed, 26 Apr 2023 14:56:02 +0100 Subject: [PATCH 11/20] add getter for AudioScape file name --- crogine/include/crogine/audio/AudioScape.hpp | 6 ++++++ crogine/src/audio/AudioScape.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/crogine/include/crogine/audio/AudioScape.hpp b/crogine/include/crogine/audio/AudioScape.hpp index 4f4e020b6..183a31271 100644 --- a/crogine/include/crogine/audio/AudioScape.hpp +++ b/crogine/include/crogine/audio/AudioScape.hpp @@ -103,8 +103,14 @@ namespace cro */ bool hasEmitter(const std::string& name) const; + /*! + \brief Returns the name from which this AudioScape was loaded, or an empty string + */ + const std::string& getName() const { return m_name; } + private: AudioResource* m_audioResource; + std::string m_name; struct AudioConfig final { diff --git a/crogine/src/audio/AudioScape.cpp b/crogine/src/audio/AudioScape.cpp index ed1f6a953..05744aa00 100644 --- a/crogine/src/audio/AudioScape.cpp +++ b/crogine/src/audio/AudioScape.cpp @@ -46,6 +46,7 @@ bool AudioScape::loadFromFile(const std::string& path, AudioResource& audioResou { m_audioResource = &audioResource; m_configs.clear(); + m_name.clear(); const auto& objs = cfg.getObjects(); for (const auto& obj : objs) @@ -130,6 +131,7 @@ bool AudioScape::loadFromFile(const std::string& path, AudioResource& audioResou LogW << "No valid AudioScape definitions were loaded from " << path << std::endl; return false; } + m_name = cro::FileSystem::getFileName(path); return true; } From a45f19a312e6ee49837c7081f872c71991246779 Mon Sep 17 00:00:00 2001 From: fallahn Date: Thu, 27 Apr 2023 14:42:30 +0100 Subject: [PATCH 12/20] parse user avatar directory on load --- samples/golf/src/golf/MenuCustomisation.cpp | 135 +++++++++++--------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/samples/golf/src/golf/MenuCustomisation.cpp b/samples/golf/src/golf/MenuCustomisation.cpp index 1d55e5ec5..c627a5ef4 100644 --- a/samples/golf/src/golf/MenuCustomisation.cpp +++ b/samples/golf/src/golf/MenuCustomisation.cpp @@ -468,90 +468,111 @@ void MenuState::parseAvatarDirectory() { m_sharedData.avatarInfo.clear(); - const std::string AvatarPath = "assets/golf/avatars/"; - - auto files = cro::FileSystem::listFiles(cro::FileSystem::getResourcePath() + AvatarPath); - m_playerAvatars.reserve(files.size()); - - for (const auto& file : files) + //path strings must include trailing "/"!! + const auto processAvatarList = + [&](const std::vector& fileList, const std::string& searchPath, const std::string resourcePath = "") { - if (cro::FileSystem::getFileExtension(file) == ".avt") + for (const auto& file : fileList) { - cro::ConfigFile cfg; - if (cfg.loadFromFile(AvatarPath + file)) + if (cro::FileSystem::getFileExtension(file) == ".avt") { - SharedStateData::AvatarInfo info; - - const auto& props = cfg.getProperties(); - for (const auto& prop : props) + cro::ConfigFile cfg; + if (cfg.loadFromFile(searchPath + file)) { - const auto& name = prop.getName(); - if (name == "model") + SharedStateData::AvatarInfo info; + + const auto& props = cfg.getProperties(); + for (const auto& prop : props) { - info.modelPath = prop.getValue(); - if (!info.modelPath.empty()) + const auto& name = prop.getName(); + if (name == "model") { - cro::ConfigFile modelData; - modelData.loadFromFile(info.modelPath); - for (const auto& o : modelData.getObjects()) + info.modelPath = resourcePath + prop.getValue(); + if (!info.modelPath.empty()) { - if (o.getName() == "material") + cro::ConfigFile modelData; + modelData.loadFromFile(info.modelPath); + for (const auto& o : modelData.getObjects()) { - for (const auto& p : o.getProperties()) + if (o.getName() == "material") { - if (p.getName() == "diffuse") + for (const auto& p : o.getProperties()) { - info.texturePath = p.getValue(); + if (p.getName() == "diffuse") + { + info.texturePath = resourcePath + p.getValue(); + } } } } } } + else if (name == "audio") + { + info.audioscape = prop.getValue(); + } + else if (name == "uid") + { + info.uid = prop.getValue(); + } } - else if (name == "audio") - { - info.audioscape = prop.getValue(); - } - else if (name == "uid") - { - info.uid = prop.getValue(); - } - } - if (!info.modelPath.empty()) - { - if (info.uid == 0) + if (!info.modelPath.empty()) { - //create a uid from the file name and save it to the cfg - //uses Bob Jenkins' spooky hash - info.uid = SpookyHash::Hash32(file.data(), file.size(), 0); - cfg.addProperty("uid").setValue(info.uid); - cfg.save(AvatarPath + file); - } - - //check uid doesn't exist - auto result = std::find_if(m_sharedData.avatarInfo.begin(), m_sharedData.avatarInfo.end(), - [&info](const SharedStateData::AvatarInfo& i) + //TODO we probably want to remove this + //and reject files instead as improperly authored + if (info.uid == 0) { - return info.uid == i.uid; - }); + //create a uid from the file name and save it to the cfg + //uses Bob Jenkins' spooky hash + info.uid = SpookyHash::Hash32(file.data(), file.size(), 0); + cfg.addProperty("uid").setValue(info.uid); + cfg.save(searchPath + file); + } - if (result == m_sharedData.avatarInfo.end()) - { - m_sharedData.avatarInfo.push_back(info); - m_playerAvatars.emplace_back(info.texturePath); + //check uid doesn't exist + auto result = std::find_if(m_sharedData.avatarInfo.begin(), m_sharedData.avatarInfo.end(), + [&info](const SharedStateData::AvatarInfo& i) + { + return info.uid == i.uid; + }); + + if (result == m_sharedData.avatarInfo.end()) + { + m_sharedData.avatarInfo.push_back(info); + m_playerAvatars.emplace_back(info.texturePath); + } + else + { + LogW << "Avatar with UID " << info.uid << " already exists. " << info.modelPath << " will be skipped." << std::endl; + } } else { - LogW << "Avatar with UID " << info.uid << " already exists. " << info.modelPath << " will be skipped." << std::endl; + LogW << "Skipping " << file << ": missing or corrupt data, or not an avatar." << std::endl; } } - else - { - LogW << "Skipping " << file << ": missing or corrupt data, or not an avatar." << std::endl; - } } } + }; + + const std::string AvatarPath = "assets/golf/avatars/"; + auto files = cro::FileSystem::listFiles(cro::FileSystem::getResourcePath() + AvatarPath); + m_playerAvatars.reserve(files.size()); + processAvatarList(files, AvatarPath); + + //custom avatars + auto avatarUserDir = Social::getUserContentPath(Social::UserContent::Avatar); + if (cro::FileSystem::directoryExists(avatarUserDir)) + { + auto dirs = cro::FileSystem::listDirectories(avatarUserDir); + dirs.resize(std::min(dirs.size(), std::size_t(24)));//arbitrary limit + for (const auto& dir : dirs) + { + auto resourceDir = avatarUserDir + dir + "/"; + files = cro::FileSystem::listFiles(resourceDir); + processAvatarList(files, resourceDir, resourceDir); + } } From 197f29f49373397fa6c3e8d8172e1fd0f39ace9b Mon Sep 17 00:00:00 2001 From: fallahn Date: Thu, 27 Apr 2023 15:29:31 +0100 Subject: [PATCH 13/20] boilerplate for installing remote avatars --- samples/golf/src/golf/MenuCreation.cpp | 10 ++++++++++ samples/golf/src/golf/MenuCustomisation.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/samples/golf/src/golf/MenuCreation.cpp b/samples/golf/src/golf/MenuCreation.cpp index 1f5bce322..e97f20651 100644 --- a/samples/golf/src/golf/MenuCreation.cpp +++ b/samples/golf/src/golf/MenuCreation.cpp @@ -2263,6 +2263,10 @@ void MenuState::updateLobbyData(const net::NetEvent& evt) //check the new player data for UGC for (auto i = 0u; i < cd.playerCount; ++i) { + //hmmm using 0 as the return value seems suspicious + //as it's not strictly an invalid index - however + //we will fall back to this *anyway* if the remote content + //is unavailable. if (indexFromBallID(cd.playerData[i].ballID) == 0) { //no local ball for this player @@ -2274,6 +2278,12 @@ void MenuState::updateLobbyData(const net::NetEvent& evt) //no local hair model Social::fetchRemoteContent(cd.peerID, cd.playerData[i].hairID, Social::UserContent::Hair); } + + if (indexFromAvatarID(cd.playerData[i].skinID) == 0) + { + //no local avatar model + Social::fetchRemoteContent(cd.peerID, cd.playerData[i].skinID, Social::UserContent::Avatar); + } } #endif } diff --git a/samples/golf/src/golf/MenuCustomisation.cpp b/samples/golf/src/golf/MenuCustomisation.cpp index c627a5ef4..cd8746750 100644 --- a/samples/golf/src/golf/MenuCustomisation.cpp +++ b/samples/golf/src/golf/MenuCustomisation.cpp @@ -979,6 +979,16 @@ void MenuState::ugcInstalledHandler(std::uint64_t id, std::int32_t type) } } } + else if (type == Social::UserContent::Avatar) + { + //insert into m_sharedData.avatarInfo so GolfState can find it + + //seach all remote players (we can skip our own client) for + //any who use this skinID, then use a ProfileTexture to load + //the avatar image and apply client/player colours to + //m_sharedData.avatarTextures[client][player] + + } else { LogE << "Unknown UGC: " << id << ", " << type << std::endl; From 43b582c7a597915c16e045e8bcde352aca4a74b2 Mon Sep 17 00:00:00 2001 From: fallahn Date: Thu, 27 Apr 2023 19:18:15 +0100 Subject: [PATCH 14/20] hack around apple's inadequacies --- samples/golf/src/golf/GolfState.cpp | 5 +++++ samples/golf/src/golf/MenuState.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 9b84ed1e5..0880b2258 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -3953,7 +3953,12 @@ void GolfState::buildScene() cro::RenderTarget::Context ctx; ctx.depthBuffer = true; +#ifdef __APPLE__ + //*sigh* + ctx.depthTexture = false; +#else ctx.depthTexture = true; +#endif ctx.samples = samples; ctx.width = static_cast(texSize.x); ctx.height = static_cast(texSize.y); diff --git a/samples/golf/src/golf/MenuState.cpp b/samples/golf/src/golf/MenuState.cpp index e87336dfd..b3b97774e 100644 --- a/samples/golf/src/golf/MenuState.cpp +++ b/samples/golf/src/golf/MenuState.cpp @@ -1360,7 +1360,7 @@ void MenuState::createScene() std::uint32_t samples = m_sharedData.pixelScale ? 0 : m_sharedData.antialias ? m_sharedData.multisamples : 0; - cro::RenderTarget::Context ctx(static_cast(texSize.x), static_cast(texSize.y), true, true, false, samples); + cro::RenderTarget::Context ctx(static_cast(texSize.x), static_cast(texSize.y), true, false, false, samples); m_sharedData.antialias = m_backgroundTexture.create(ctx) From 8af82544820092b0c31488fe8112864604106440 Mon Sep 17 00:00:00 2001 From: fallahn Date: Fri, 28 Apr 2023 18:55:12 +0100 Subject: [PATCH 15/20] fix player scores not always entering into leaderboard in online games --- crogine/src/ecs/components/Drawable2D.cpp | 2 +- samples/golf/src/golf/GolfState.cpp | 9 +++++---- samples/golf/src/golf/GolfState.hpp | 1 + samples/golf/src/golf/GolfStateUI.cpp | 7 +++++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crogine/src/ecs/components/Drawable2D.cpp b/crogine/src/ecs/components/Drawable2D.cpp index 9b3dc28bd..23f532a63 100644 --- a/crogine/src/ecs/components/Drawable2D.cpp +++ b/crogine/src/ecs/components/Drawable2D.cpp @@ -253,7 +253,7 @@ void Drawable2D::applyShader() else { m_textureUniform = -1; - Logger::log("Missing texture uniform in Drawable2D shader", Logger::Type::Error); + Logger::log("Missing texture uniform in Drawable2D shader", Logger::Type::Warning); return; } } diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 0880b2258..e43cc7f06 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -148,7 +148,7 @@ namespace #endif // CRO_DEBUG_ float godmode = 1.f; - bool allowAchievements = false; + const cro::Time DefaultIdleTime = cro::seconds(180.f); cro::Time idleTime = DefaultIdleTime; @@ -189,6 +189,7 @@ GolfState::GolfState(cro::StateStack& stack, cro::State::Context context, Shared m_inputParser (sd, &m_gameScene), m_cpuGolfer (m_inputParser, m_currentPlayer, m_collisionMesh), m_wantsGameState (true), + m_allowAchievements (false), m_scaleBuffer ("PixelScale"), m_resolutionBuffer ("ScaledResolution"), m_windBuffer ("WindValues"), @@ -269,10 +270,10 @@ GolfState::GolfState(cro::StateStack& stack, cro::State::Context context, Shared humanCount++; } } - allowAchievements = humanCount == 1; + m_allowAchievements = humanCount == 1; //This is set when setting active player. - Achievements::setActive(allowAchievements); + Achievements::setActive(m_allowAchievements); //do this first so scores are reset before scoreboard //is first created. @@ -6145,7 +6146,7 @@ void GolfState::setCurrentPlayer(const ActivePlayer& player) m_sharedData.inputBinding.playerID = localPlayer ? player.player : 0; //this also affects who can emote, so if we're currently emoting when it's not our turn always be player 0(??) m_inputParser.setActive(localPlayer && !m_photoMode, m_currentPlayer.terrain, isCPU); m_restoreInput = localPlayer; //if we're in photo mode should we restore input parser? - Achievements::setActive(localPlayer && !isCPU && allowAchievements); + Achievements::setActive(localPlayer && !isCPU && m_allowAchievements); if (player.terrain == TerrainID::Bunker) { diff --git a/samples/golf/src/golf/GolfState.hpp b/samples/golf/src/golf/GolfState.hpp index cb8adf073..2865e158c 100644 --- a/samples/golf/src/golf/GolfState.hpp +++ b/samples/golf/src/golf/GolfState.hpp @@ -126,6 +126,7 @@ class GolfState final : public cro::State, public cro::GuiClient, public cro::Co cro::Clock m_idleTimer; bool m_wantsGameState; + bool m_allowAchievements; cro::Clock m_readyClock; //pings ready state until ack'd cro::RenderTexture m_gameSceneTexture; diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index f40f8fb4c..9c204640e 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -1447,8 +1447,11 @@ void GolfState::showCountdown(std::uint8_t seconds) personalBest = true; } + //if we weren't the last player to take a turn in a network game + //we need to reenable achievements to enter into the leaderboard... + Achievements::setActive(m_allowAchievements); + cro::Logger::log("LEADERBOARD attempting to insert score: " + std::to_string(score) + "\n", cro::Logger::Type::Info, cro::Logger::Output::File); Social::insertScore(m_sharedData.mapDirectory, m_sharedData.holeCount, score); - cro::Logger::log("LEADERBOARD attempting to insert score: " + std::to_string(connectionData.playerData[k].score), cro::Logger::Type::Info, cro::Logger::Output::File); break; } } @@ -1538,7 +1541,7 @@ void GolfState::showCountdown(std::uint8_t seconds) m_uiScene.getSystem()->sendCommand(cmd); } - + //create status icons for each connected client //to show vote to skip From b3242601d3ccdff92f217e03dedeaa386549b56f Mon Sep 17 00:00:00 2001 From: fallahn Date: Sat, 29 Apr 2023 11:39:45 +0100 Subject: [PATCH 16/20] fix path of default avatar image for non-steam builds --- samples/golf/mod_kit/avatar_keys.ase | Bin 364 -> 276 bytes samples/golf/src/GolfGame.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/golf/mod_kit/avatar_keys.ase b/samples/golf/mod_kit/avatar_keys.ase index d49a43ea288ec1909fef423186e10991bd70a1c1..c3e977d4ad8aeface2f8a9329c803648de014993 100644 GIT binary patch literal 276 zcmZ<^c6DQ5WB>s+Fs;MD!{Ee_&rkw{`3$)X3Jfj`i3~*y*$fOp?oJ9|l?Vc)Q;&fc zO@|LdCPO+y2G9hs9$SS43@{8-57MK?z{wB-G`|4deA^En7+@HxR)c{Ht`^N?JD|xh r3{@@1z{(H|bZsU>9>jLLo*o7mMpEMe^i?L%Utky7ojAb&!wd`nQ0p(c literal 364 zcmZ<^c6DQ5WB>sUFs;VG$q>v?&XCAZ!jR06!Jxok$iNWf?xX-#gCIb9Fx47C)Y>X6 zV1QwuI_zqVp=v*TV6cT^oN7%VYVCmL!Z1#?rckv79~kVQ7^hk@sM?+$20JLmsn#5- YcFPt9J1EAf)&i>b#0dsFC}v;)00j6y^#A|> diff --git a/samples/golf/src/GolfGame.cpp b/samples/golf/src/GolfGame.cpp index 4daf9ab0a..0dd6bfc92 100644 --- a/samples/golf/src/GolfGame.cpp +++ b/samples/golf/src/GolfGame.cpp @@ -408,7 +408,7 @@ bool GolfGame::initialise() cro::Logger::log("No suitable host addresses were found", cro::Logger::Type::Error, cro::Logger::Output::All); return false; } - Social::userIcon = cropAvatarImage("assets/workshop/profile_template.png"); + Social::userIcon = cropAvatarImage("assets/images/default_profile.png"); #endif parseCredits(); From 94f59ecc6cc24922e0be06287f23e0b72f7f224c Mon Sep 17 00:00:00 2001 From: fallahn Date: Sat, 29 Apr 2023 12:53:44 +0100 Subject: [PATCH 17/20] fix craash when clients leave mid game caused by invalid avatar icon --- samples/golf/src/golf/GolfStateUI.cpp | 1 + samples/golf/src/golf/MenuAvatars.cpp | 3 + samples/golf/src/golf/MenuCustomisation.cpp | 192 ++++++++++---------- samples/golf/src/golf/MenuState.hpp | 1 + 4 files changed, 101 insertions(+), 96 deletions(-) diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index 9c204640e..fc87e0c74 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -1932,6 +1932,7 @@ void GolfState::createScoreboard() { e.getComponent().active = false; m_uiScene.destroyEntity(e); + return; } //these are set on the ent by updating the scoreboard, rather than rearranging entity positions diff --git a/samples/golf/src/golf/MenuAvatars.cpp b/samples/golf/src/golf/MenuAvatars.cpp index 7f0c7ab10..39b7129cb 100644 --- a/samples/golf/src/golf/MenuAvatars.cpp +++ b/samples/golf/src/golf/MenuAvatars.cpp @@ -1725,6 +1725,9 @@ void MenuState::refreshProfileFlyout() void MenuState::updateLobbyAvatars() { + //TODO we need to refine this so only textures of the most + //recently updated client are updated + cro::Command cmd; cmd.targetFlags = CommandID::Menu::LobbyList; cmd.action = [&](cro::Entity e, float) diff --git a/samples/golf/src/golf/MenuCustomisation.cpp b/samples/golf/src/golf/MenuCustomisation.cpp index cd8746750..8a569c562 100644 --- a/samples/golf/src/golf/MenuCustomisation.cpp +++ b/samples/golf/src/golf/MenuCustomisation.cpp @@ -468,94 +468,6 @@ void MenuState::parseAvatarDirectory() { m_sharedData.avatarInfo.clear(); - //path strings must include trailing "/"!! - const auto processAvatarList = - [&](const std::vector& fileList, const std::string& searchPath, const std::string resourcePath = "") - { - for (const auto& file : fileList) - { - if (cro::FileSystem::getFileExtension(file) == ".avt") - { - cro::ConfigFile cfg; - if (cfg.loadFromFile(searchPath + file)) - { - SharedStateData::AvatarInfo info; - - const auto& props = cfg.getProperties(); - for (const auto& prop : props) - { - const auto& name = prop.getName(); - if (name == "model") - { - info.modelPath = resourcePath + prop.getValue(); - if (!info.modelPath.empty()) - { - cro::ConfigFile modelData; - modelData.loadFromFile(info.modelPath); - for (const auto& o : modelData.getObjects()) - { - if (o.getName() == "material") - { - for (const auto& p : o.getProperties()) - { - if (p.getName() == "diffuse") - { - info.texturePath = resourcePath + p.getValue(); - } - } - } - } - } - } - else if (name == "audio") - { - info.audioscape = prop.getValue(); - } - else if (name == "uid") - { - info.uid = prop.getValue(); - } - } - - if (!info.modelPath.empty()) - { - //TODO we probably want to remove this - //and reject files instead as improperly authored - if (info.uid == 0) - { - //create a uid from the file name and save it to the cfg - //uses Bob Jenkins' spooky hash - info.uid = SpookyHash::Hash32(file.data(), file.size(), 0); - cfg.addProperty("uid").setValue(info.uid); - cfg.save(searchPath + file); - } - - //check uid doesn't exist - auto result = std::find_if(m_sharedData.avatarInfo.begin(), m_sharedData.avatarInfo.end(), - [&info](const SharedStateData::AvatarInfo& i) - { - return info.uid == i.uid; - }); - - if (result == m_sharedData.avatarInfo.end()) - { - m_sharedData.avatarInfo.push_back(info); - m_playerAvatars.emplace_back(info.texturePath); - } - else - { - LogW << "Avatar with UID " << info.uid << " already exists. " << info.modelPath << " will be skipped." << std::endl; - } - } - else - { - LogW << "Skipping " << file << ": missing or corrupt data, or not an avatar." << std::endl; - } - } - } - } - }; - const std::string AvatarPath = "assets/golf/avatars/"; auto files = cro::FileSystem::listFiles(cro::FileSystem::getResourcePath() + AvatarPath); m_playerAvatars.reserve(files.size()); @@ -710,6 +622,93 @@ void MenuState::parseAvatarDirectory() createAvatarScene(); } +void MenuState::processAvatarList(const std::vector& fileList, const std::string& searchPath, const std::string resourcePath) +{ + //path strings must include trailing "/"!! + for (const auto& file : fileList) + { + if (cro::FileSystem::getFileExtension(file) == ".avt") + { + cro::ConfigFile cfg; + if (cfg.loadFromFile(searchPath + file)) + { + SharedStateData::AvatarInfo info; + + const auto& props = cfg.getProperties(); + for (const auto& prop : props) + { + const auto& name = prop.getName(); + if (name == "model") + { + info.modelPath = resourcePath + prop.getValue(); + if (!info.modelPath.empty()) + { + cro::ConfigFile modelData; + modelData.loadFromFile(info.modelPath); + for (const auto& o : modelData.getObjects()) + { + if (o.getName() == "material") + { + for (const auto& p : o.getProperties()) + { + if (p.getName() == "diffuse") + { + info.texturePath = resourcePath + p.getValue(); + } + } + } + } + } + } + else if (name == "audio") + { + info.audioscape = prop.getValue(); + } + else if (name == "uid") + { + info.uid = prop.getValue(); + } + } + + if (!info.modelPath.empty()) + { + //TODO we probably want to remove this + //and reject files instead as improperly authored + if (info.uid == 0) + { + //create a uid from the file name and save it to the cfg + //uses Bob Jenkins' spooky hash + info.uid = SpookyHash::Hash32(file.data(), file.size(), 0); + cfg.addProperty("uid").setValue(info.uid); + cfg.save(searchPath + file); + } + + //check uid doesn't exist + auto result = std::find_if(m_sharedData.avatarInfo.begin(), m_sharedData.avatarInfo.end(), + [&info](const SharedStateData::AvatarInfo& i) + { + return info.uid == i.uid; + }); + + if (result == m_sharedData.avatarInfo.end()) + { + m_sharedData.avatarInfo.push_back(info); + m_playerAvatars.emplace_back(info.texturePath); + } + else + { + LogW << "Avatar with UID " << info.uid << " already exists. " << info.modelPath << " will be skipped." << std::endl; + } + } + else + { + LogW << "Skipping " << file << ": missing or corrupt data, or not an avatar." << std::endl; + } + } + } + } +} + void MenuState::createAvatarScene() { auto avatarTexCallback = [&](cro::Camera& cam) @@ -939,7 +938,7 @@ void MenuState::ugcInstalledHandler(std::uint64_t id, std::int32_t type) //models for remote players who have them. const auto BallUserPath = Social::getUserContentPath(Social::UserContent::Ball) + std::to_string(id) + "/"; auto files = cro::FileSystem::listFiles(BallUserPath); - + LogI << "installed remote ball" << std::endl; for (const auto& file : files) { if (cro::FileSystem::getFileExtension(file) == ".ball") @@ -961,7 +960,7 @@ void MenuState::ugcInstalledHandler(std::uint64_t id, std::int32_t type) { const auto HairUserPath = Social::getUserContentPath(Social::UserContent::Hair) + std::to_string(id) + "/"; auto files = cro::FileSystem::listFiles(HairUserPath); - + LogI << "installed remote hair" << std::endl; for (const auto& file : files) { if (cro::FileSystem::getFileExtension(file) == ".hct") @@ -982,12 +981,13 @@ void MenuState::ugcInstalledHandler(std::uint64_t id, std::int32_t type) else if (type == Social::UserContent::Avatar) { //insert into m_sharedData.avatarInfo so GolfState can find it - - //seach all remote players (we can skip our own client) for - //any who use this skinID, then use a ProfileTexture to load - //the avatar image and apply client/player colours to - //m_sharedData.avatarTextures[client][player] - + const auto& avatarPath = Social::getUserContentPath(Social::UserContent::Avatar) + std::to_string(id) + "/"; + auto files = cro::FileSystem::listFiles(avatarPath); + processAvatarList(files, avatarPath, avatarPath); + LogI << "Installed remote avatar" << std::endl; + //this just updates all the textures including the newly acquired + //avatar data - there's room for optimisation here. + updateLobbyAvatars(); } else { diff --git a/samples/golf/src/golf/MenuState.hpp b/samples/golf/src/golf/MenuState.hpp index c4d02e73c..3ed18fca1 100644 --- a/samples/golf/src/golf/MenuState.hpp +++ b/samples/golf/src/golf/MenuState.hpp @@ -284,6 +284,7 @@ class MenuState final : public cro::State, public cro::GuiClient, public cro::Co cro::RenderTexture m_avatarTexture; void parseAvatarDirectory(); + void processAvatarList(const std::vector&, const std::string&, const std::string = ""); void createAvatarScene(); std::int32_t indexFromAvatarID(std::uint32_t); void ugcInstalledHandler(std::uint64_t id, std::int32_t type); From be09754f77d07f6462ba727bfde19fa2feafd56e Mon Sep 17 00:00:00 2001 From: fallahn Date: Sat, 29 Apr 2023 13:29:27 +0100 Subject: [PATCH 18/20] fix crash caused by quitting clients not removing mini-putt icons --- samples/golf/src/golf/GolfState.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index e43cc7f06..54a06a541 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -5063,6 +5063,13 @@ void GolfState::spawnBall(const ActorInfo& info) entity.getComponent().function = [&, ballEnt, depthOffset](cro::Entity e, float) { + if (ballEnt.destroyed()) + { + e.getComponent().active = false; + m_uiScene.destroyEntity(e); + return; + } + if (m_currentPlayer.terrain == TerrainID::Green) { auto pos = ballEnt.getComponent().getWorldPosition(); From 0c756b1c6a2288b9e311a9f9fe01332366cbe6fb Mon Sep 17 00:00:00 2001 From: fallahn Date: Sat, 29 Apr 2023 15:53:28 +0100 Subject: [PATCH 19/20] update version number --- samples/golf/golf.aps | Bin 295048 -> 294872 bytes samples/golf/golf.rc | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/golf/golf.aps b/samples/golf/golf.aps index 34da611bba73a107fa987db1e74ade6d53d84f62..3c2d267c89f168d835a1dd4f80942eef5801075d 100644 GIT binary patch delta 307 zcmXYnJxjx26ov17b5pT)v4bFL(%OQhhy@1+@gswygWw_}Zbi4&P=rR4R4V8s#uV-n zh3s8~Lbm<__X_GF9UOEKTpWyV(Bbf$=Ws61vXAljbq>p3zA*N1O0coKXIASq&&uBi zJBWe*g*WuK=$UmWLL4U{>MW3;RS|)Bpeg delta 462 zcmb76-z!657=GXHyeZL&3sKBw3JDuhcB$Qt)tXXATH7urcCvGhGTT|ANSrq3;`KMU z5Le5cyK#XV#UH_id&PV57x=#3@A>gQ&-eb*b8^tz2w|ofjx1Db1c|twF$-l=Pi8Gk zPZ&9SyHK)BeYI%X*{x98vhzhFG#iaA>Z!7^S;$$HV@nYtmWp6`GXb?y5 zRE@7^z=Q&n<<%hxS+L~lkN^X6V8gcTO5*t2RVWJDlG%a~q{X!%FPqhz#}t5IXO6QCOq1%@(dCj3A4mi|@`9Hqo^BbSf z;VYjzG_3S4zIG_6MhE#>5I^|Sx95*IP#5{lQ+1JvFfWT#XC{|?gbKTLn$|8W#4md^ F@C)L|gVz86 diff --git a/samples/golf/golf.rc b/samples/golf/golf.rc index ac5ec0a3a..47493abf7 100644 --- a/samples/golf/golf.rc +++ b/samples/golf/golf.rc @@ -61,8 +61,8 @@ IDI_ICON1 ICON "icon.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,12,0,0 - PRODUCTVERSION 1,12,0,0 + FILEVERSION 1,12,2,0 + PRODUCTVERSION 1,12,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "Trederia" VALUE "FileDescription", "Super Video Golf" - VALUE "FileVersion", "1.12.0.0" + VALUE "FileVersion", "1.12.2.0" VALUE "InternalName", "golf.exe" VALUE "LegalCopyright", "Copyright (C) 2023 Trederia Games" VALUE "OriginalFilename", "golf.exe" VALUE "ProductName", "Super Video Golf" - VALUE "ProductVersion", "1.12.0.0" + VALUE "ProductVersion", "1.12.2.0" END END BLOCK "VarFileInfo" From 8be70105b8eb888182c04443ed948d91e9a07d0f Mon Sep 17 00:00:00 2001 From: fallahn Date: Sun, 30 Apr 2023 13:06:35 +0100 Subject: [PATCH 20/20] pad out RGB images to RGBA on load for correct row alignment in textures --- crogine/src/core/App.cpp | 2 +- crogine/src/graphics/ImageArray.cpp | 40 ++++++++++++++++++++++++-- crogine/src/graphics/Texture.cpp | 11 +++++-- samples/golf/src/golf/GolfState.cpp | 1 - samples/golf/src/golf/MenuState.cpp | 8 +++--- samples/golf/src/golf/ProfileState.cpp | 2 +- samples/scratchpad/src/MyApp.cpp | 4 +-- 7 files changed, 53 insertions(+), 15 deletions(-) diff --git a/crogine/src/core/App.cpp b/crogine/src/core/App.cpp index b1cab218f..8b603ee8d 100644 --- a/crogine/src/core/App.cpp +++ b/crogine/src/core/App.cpp @@ -68,7 +68,7 @@ void winAbort(int) #include #ifdef CRO_DEBUG_ -//#define DEBUG_NO_CONTROLLER +#define DEBUG_NO_CONTROLLER #endif // CRO_DEBUG_ using namespace cro; diff --git a/crogine/src/graphics/ImageArray.cpp b/crogine/src/graphics/ImageArray.cpp index 0bbbe388e..ac9d6c54d 100644 --- a/crogine/src/graphics/ImageArray.cpp +++ b/crogine/src/graphics/ImageArray.cpp @@ -53,9 +53,20 @@ namespace cro::Detail stbi_callback_from_RW(file, &io); std::int32_t w, h, d; - auto* img = stbi_load_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, 0); + stbi_info_from_callbacks(&io.stb_cbs, &io, &w, &h, &d); + file->seek(file, 0, RW_SEEK_SET); + + //if this is RGB pad out to RGBA for row alignment + std::int32_t wantedChannels = 0; + if (d == 3) + { + wantedChannels = 4; + } + + auto* img = stbi_load_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, wantedChannels); if (img) { + d = (wantedChannels) ? wantedChannels : d; auto size = w * h * d; dst.resize(size); @@ -95,9 +106,21 @@ namespace cro::Detail stbi_callback_from_RW(file, &io); std::int32_t w, h, d; - auto* img = stbi_load_16_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, 0); + stbi_info_from_callbacks(&io.stb_cbs, &io, &w, &h, &d); + file->seek(file, 0, RW_SEEK_SET); + + //if this is RGB pad out to RGBA for row alignment + std::int32_t wantedChannels = 0; + if (d == 3) + { + wantedChannels = 4; + } + + + auto* img = stbi_load_16_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, wantedChannels); if (img) { + d = (wantedChannels) ? wantedChannels : d; auto size = w * h * d; dst.resize(size); @@ -137,9 +160,20 @@ namespace cro::Detail stbi_callback_from_RW(file, &io); std::int32_t w, h, d; - auto* img = stbi_loadf_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, 0); + stbi_info_from_callbacks(&io.stb_cbs, &io, &w, &h, &d); + file->seek(file, 0, RW_SEEK_SET); + + //if this is RGB pad out to RGBA for row alignment + std::int32_t wantedChannels = 0; + if (d == 3) + { + wantedChannels = 4; + } + + auto* img = stbi_loadf_from_callbacks(&io.stb_cbs, &io, &w, &h, &d, wantedChannels); if (img) { + d = (wantedChannels) ? wantedChannels : 0; auto size = w * h * d; dst.resize(size); diff --git a/crogine/src/graphics/Texture.cpp b/crogine/src/graphics/Texture.cpp index e27ca3a6d..b6abd6706 100644 --- a/crogine/src/graphics/Texture.cpp +++ b/crogine/src/graphics/Texture.cpp @@ -161,6 +161,7 @@ void Texture::create(std::uint32_t width, std::uint32_t height, ImageFormat::Typ pixelSize = 1; } + //let's fill the texture with known empty values std::vector buffer(width * height * pixelSize); std::fill(buffer.begin(), buffer.end(), 0); @@ -187,7 +188,10 @@ bool Texture::loadFromFile(const std::string& filePath, bool createMipMaps) ImageArray arr; if (arr.loadFromFile(path, true)) { + m_type = GL_UNSIGNED_BYTE; + auto size = arr.getDimensions(); + CRO_ASSERT(size.x * size.y * arr.getChannels() == arr.size(), ""); create(size.x, size.y, arr.getFormat()); return update(arr.data(), createMipMaps); } @@ -470,16 +474,17 @@ bool Texture::update(const void* pixels, bool createMipMaps, URect area) if (area.width == 0) area.width = m_size.x; if (area.height == 0) area.height = m_size.y; - GLint format = GL_RGBA; - if (m_format == ImageFormat::RGB) + GLint format = GL_RGB; + if (m_format == ImageFormat::RGBA) { - format = GL_RGB; + format = GL_RGBA; } else if (m_format == ImageFormat::A) { format = GL_RED; } + glCheck(glBindTexture(GL_TEXTURE_2D, m_handle)); glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, area.left, area.bottom, area.width, area.height, format, m_type, pixels)); diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 54a06a541..c9167b534 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -4949,7 +4949,6 @@ void GolfState::spawnBall(const ActorInfo& info) cro::Vertex2D(glm::vec2(0.f), glm::vec2(0.f, uvRect.bottom), BaseColour), cro::Vertex2D(glm::vec2(textureRect.width, 0.f), glm::vec2(uvRect.width, uvRect.bottom), BaseColour), - //TODO in the non-steam version this is mostly just a waste, unless an avatar has a specific head shot cro::Vertex2D(AvatarOffset + glm::vec2(0.f, AvatarSize.y), glm::vec2(avatarUV.left, avatarUV.bottom + avatarUV.height), BaseColour), cro::Vertex2D(AvatarOffset + glm::vec2(0.f), glm::vec2(avatarUV.left, avatarUV.bottom), BaseColour), cro::Vertex2D(AvatarOffset + AvatarSize, glm::vec2(avatarUV.left + avatarUV.width, avatarUV.bottom + avatarUV.height), BaseColour), diff --git a/samples/golf/src/golf/MenuState.cpp b/samples/golf/src/golf/MenuState.cpp index b3b97774e..04fab2dc8 100644 --- a/samples/golf/src/golf/MenuState.cpp +++ b/samples/golf/src/golf/MenuState.cpp @@ -125,7 +125,7 @@ MenuState::MenuState(cro::StateStack& stack, cro::State::Context context, Shared m_cursor (/*"assets/images/cursor.png", 0, 0*/cro::SystemCursor::Hand), m_uiScene (context.appInstance.getMessageBus(), 512), m_backgroundScene (context.appInstance.getMessageBus(), 512/*, cro::INFO_FLAG_SYSTEMS_ACTIVE*/), - m_avatarScene (context.appInstance.getMessageBus()/*, 128, cro::INFO_FLAG_SYSTEMS_ACTIVE*/), + m_avatarScene (context.appInstance.getMessageBus(), 384/*, cro::INFO_FLAG_SYSTEMS_ACTIVE*/), m_scaleBuffer ("PixelScale"), m_resolutionBuffer ("ScaledResolution"), m_windBuffer ("WindValues"), @@ -136,7 +136,7 @@ MenuState::MenuState(cro::StateStack& stack, cro::State::Context context, Shared m_viewScale (1.f) { std::fill(m_readyState.begin(), m_readyState.end(), false); - + auto size = glm::vec2(GolfGame::getActiveTarget()->getSize()); m_viewScale = glm::vec2(getViewScale()); @@ -359,12 +359,12 @@ MenuState::MenuState(cro::StateStack& stack, cro::State::Context context, Shared // { // if (ImGui::Begin("buns")) // { - // auto size = glm::vec2(LabelTextureSize); + // /*auto size = glm::vec2(LabelTextureSize); // for (const auto& t : m_sharedData.nameTextures) // { // ImGui::Image(t.getTexture(), { size.x, size.y }, { 0.f, 1.f }, { 1.f, 0.f }); // ImGui::SameLine(); - // } + // }*/ // } // ImGui::End(); // }); diff --git a/samples/golf/src/golf/ProfileState.cpp b/samples/golf/src/golf/ProfileState.cpp index b7f1760c6..d3433fd6f 100644 --- a/samples/golf/src/golf/ProfileState.cpp +++ b/samples/golf/src/golf/ProfileState.cpp @@ -118,7 +118,7 @@ namespace ProfileState::ProfileState(cro::StateStack& ss, cro::State::Context ctx, SharedStateData& sd, SharedProfileData& sp) : cro::State (ss, ctx), m_uiScene (ctx.appInstance.getMessageBus(), 384u), - m_modelScene (ctx.appInstance.getMessageBus()), + m_modelScene (ctx.appInstance.getMessageBus(), 384u), m_sharedData (sd), m_profileData (sp), m_viewScale (2.f), diff --git a/samples/scratchpad/src/MyApp.cpp b/samples/scratchpad/src/MyApp.cpp index eae2b2584..c9c89bb15 100644 --- a/samples/scratchpad/src/MyApp.cpp +++ b/samples/scratchpad/src/MyApp.cpp @@ -124,8 +124,8 @@ bool MyApp::initialise() m_stateStack.registerState(States::ScratchPad::SSAO); #ifdef CRO_DEBUG_ - m_stateStack.pushState(States::ScratchPad::SSAO); - //m_stateStack.pushState(States::ScratchPad::MainMenu); + //m_stateStack.pushState(States::ScratchPad::SSAO); + m_stateStack.pushState(States::ScratchPad::MainMenu); #else m_stateStack.pushState(States::ScratchPad::MainMenu); #endif