Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Assets/Scripts/Loading/AvatarLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,20 @@ private bool TryFindWearableByItemId(string itemId, out GameObject owner)
owner = null;
return false;
}

public void ApplyColors(AvatarColors colors)
{
foreach (var model in _loadedModels.Values)
{
AvatarUtils.ApplySkinHairColors(model.Root, colors);
}

var bodyGO = _loadedModels.Values.FirstOrDefault(er => er.Entity.Type == EntityType.Body).Root;
if (bodyGO != null)
{
AvatarUtils.ApplyFacialFeatureColors(bodyGO, colors);
}
}

public void HideFacialFeatures()
{
Expand Down
8 changes: 8 additions & 0 deletions Assets/Scripts/Loading/WearableLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public async Awaitable LoadWearable(EntityDefinition entityDefinition, BodyShape
_outlineRenderers.Clear();
AvatarUtils.SetupWearable(_wearableGO, colors, _outlineRenderers);
}

public void ApplyColors(AvatarColors colors)
{
if (_wearableGO == null) return;

AvatarUtils.ApplySkinHairColors(_wearableGO, colors);
AvatarUtils.ApplyFacialFeatureColors(_wearableGO, colors);
}

private void Update()
{
Expand Down
29 changes: 28 additions & 1 deletion Assets/Scripts/Preview/PreviewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public class PreviewController : MonoBehaviour
private bool _loading;
private bool _shouldReload;
private bool _shouldCleanup;
private AvatarColors _marketplaceColors;
private string _marketplaceColorsProfile;

private void Start()
{
previewUIPresenter.ShowAvatarClicked += OnShowAvatarClicked;
previewUIPresenter.ShowWearableClicked += OnShowWearableClicked;
previewUIPresenter.EmoteToggleClicked += OnEmoteToggleClicked;
previewUIPresenter.AvatarColorChanged += OnAvatarColorChanged;
previewUIPresenter.ContainerDrag += avatarRotator.OnDrag;
previewUIPresenter.ContainerDrag += wearableRotator.OnDrag;
emoteAnimationController.EmoteAnimationEnded += OnEmoteAnimationEnded;
Expand Down Expand Up @@ -68,6 +71,19 @@ private void OnEmoteToggleClicked(bool playing)
}
}

private void OnAvatarColorChanged(PreviewUIPresenter.ColorTarget target, Color color)
{
if (_marketplaceColors == null) return;

_marketplaceColors = new AvatarColors(
target == PreviewUIPresenter.ColorTarget.Eyes ? color : _marketplaceColors.Eyes,
target == PreviewUIPresenter.ColorTarget.Hair ? color : _marketplaceColors.Hair,
target == PreviewUIPresenter.ColorTarget.Skin ? color : _marketplaceColors.Skin);

avatarLoader.ApplyColors(_marketplaceColors);
wearableLoader.ApplyColors(_marketplaceColors);
}

private void OnShowWearableClicked()
{
PlayerPrefs.SetInt("PreviewAvatarShown", 0);
Expand Down Expand Up @@ -249,6 +265,12 @@ await LoadForBuilder(config.BodyShape,
previewUIPresenter.EnableZoom(config.Mode is PreviewMode.Marketplace or PreviewMode.Builder);
previewUIPresenter.EnableSwitcher(hasWearableOverride);
previewUIPresenter.EnableAudioControls(hasEmoteAudio);
previewUIPresenter.EnableColorPicker(config.Mode is PreviewMode.Marketplace);

if (config.Mode is PreviewMode.Marketplace)
{
previewUIPresenter.SetColorPickerColors(_marketplaceColors);
}
} while (_shouldReload);

previewUIPresenter.ShowLoader(false);
Expand Down Expand Up @@ -320,7 +342,12 @@ await avatarLoader.LoadAvatar(bodyShape,

var avatar = await APIService.GetAvatar(profileID);
var avatarBodyShape = avatar.GetBodyShape();
var avatarColors = avatar.GetAvatarColors();

var avatarColors = _marketplaceColors != null && _marketplaceColorsProfile == profileID
? _marketplaceColors
: avatar.GetAvatarColors();
_marketplaceColors = avatarColors;
_marketplaceColorsProfile = profileID;
var allEntities = await EntityService.GetEntities(avatar.wearables.Append(urn).ToArray());
var overrideDefinition = allEntities.First(ed => ed.URN == urn);

Expand Down
133 changes: 133 additions & 0 deletions Assets/Scripts/Preview/PreviewUIPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using Data;
using UI.Elements;
using UI.Manipulators;
using UnityEngine;
using UnityEngine.InputSystem;
Expand All @@ -17,6 +18,8 @@ namespace Preview
public class PreviewUIPresenter : MonoBehaviour
{
private const string USS_SWITCHER_BUTTON_SELECTED = "switcher__button--selected";
private const string USS_COLOR_PICKER_BUTTON_ACTIVE = "color-picker__button--active";
private const string USS_COLOR_PICKER_TAB_SELECTED = "color-picker__tab--selected";
private const float LOADER_SPEED = 360f;
private const string DEBUG_PASSPHRASE = "debugmesilly";

Expand All @@ -27,6 +30,7 @@ public class PreviewUIPresenter : MonoBehaviour
public event Action ShowWearableClicked;
public event Action<bool> EmoteToggleClicked;
public event Action<Vector2, float> ContainerDrag;
public event Action<ColorTarget, Color> AvatarColorChanged;

private VisualElement _switcher;
private VisualElement _wearableButton;
Expand All @@ -41,6 +45,17 @@ public class PreviewUIPresenter : MonoBehaviour
private Label _playEmoteLabel;
private Button _muteEmoteButton;

private VisualElement _colorPicker;
private Button _colorPickerButton;
private VisualElement _colorPickerPanel;
private Button _hairTab;
private Button _eyesTab;
private Button _skinTab;
private VisualElement _colorSwatch;
private GradientSliderElement _hueSlider;
private GradientSliderElement _saturationSlider;
private GradientSliderElement _valueSlider;

private VisualElement _controls;
private VisualElement _loader;
private VisualElement _loaderIcon;
Expand All @@ -50,6 +65,8 @@ public class PreviewUIPresenter : MonoBehaviour
private bool _animationPlaying = true;
private SwitcherState _switcherState = SwitcherState.Wearable;
private float _lastPlayPauseClickTime;
private ColorTarget _colorTarget = ColorTarget.Hair;
private AvatarColors _pickerColors;

private void OnEnable()
{
Expand All @@ -71,6 +88,26 @@ private void OnEnable()
_playEmoteButton.clicked += OnPlayPauseEmoteClicked;
_muteEmoteButton.clicked += OnMuteEmoteClicked;

_colorPicker = root.Q("ColorPicker");
_colorPickerButton = _colorPicker.Q<Button>("ColorPickerButton");
_colorPickerPanel = _colorPicker.Q("ColorPickerPanel");
_hairTab = _colorPickerPanel.Q<Button>("HairTab");
_eyesTab = _colorPickerPanel.Q<Button>("EyesTab");
_skinTab = _colorPickerPanel.Q<Button>("SkinTab");
_colorSwatch = _colorPickerPanel.Q("Swatch");
_hueSlider = _colorPickerPanel.Q<GradientSliderElement>("HueSlider");
_saturationSlider = _colorPickerPanel.Q<GradientSliderElement>("SaturationSlider");
_valueSlider = _colorPickerPanel.Q<GradientSliderElement>("ValueSlider");
_hueSlider.SetHueGradient();
_colorPickerButton.clicked += ToggleColorPickerPanel;
_hairTab.clicked += () => SelectColorTarget(ColorTarget.Hair);
_eyesTab.clicked += () => SelectColorTarget(ColorTarget.Eyes);
_skinTab.clicked += () => SelectColorTarget(ColorTarget.Skin);
_hueSlider.RegisterValueChangedCallback(_ => OnColorSliderChanged());
_saturationSlider.RegisterValueChangedCallback(_ => OnColorSliderChanged());
_valueSlider.RegisterValueChangedCallback(_ => OnColorSliderChanged());
_colorPickerPanel.RegisterCallback<PointerDownEvent>(evt => evt.StopPropagation());

_controls = root.Q("Controls");
_loader = root.Q("Loader");
_loaderIcon = _loader.Q("Icon");
Expand Down Expand Up @@ -105,6 +142,23 @@ public void EnableZoom(bool enable)
_zoomControls.style.display = enable ? DisplayStyle.Flex : DisplayStyle.None;
}

public void EnableColorPicker(bool enable)
{
_colorPicker.style.display = enable ? DisplayStyle.Flex : DisplayStyle.None;

if (!enable)
{
_colorPickerPanel.style.display = DisplayStyle.None;
_colorPickerButton.EnableInClassList(USS_COLOR_PICKER_BUTTON_ACTIVE, false);
}
}

public void SetColorPickerColors(AvatarColors colors)
{
_pickerColors = colors;
SelectColorTarget(_colorTarget);
}

public void EnableEmoteControls(bool enable)
{
_emoteControls.style.display = enable ? DisplayStyle.Flex : DisplayStyle.None;
Expand Down Expand Up @@ -152,6 +206,78 @@ public void SetAnimationPlaying(bool playing)
_playEmoteLabel.text = _animationPlaying ? "STOP EMOTE" : "PLAY EMOTE";
}

private void ToggleColorPickerPanel()
{
var open = _colorPickerPanel.style.display == DisplayStyle.None;
_colorPickerPanel.style.display = open ? DisplayStyle.Flex : DisplayStyle.None;
_colorPickerButton.EnableInClassList(USS_COLOR_PICKER_BUTTON_ACTIVE, open);

if (open)
{
SelectColorTarget(_colorTarget);
}
}

private void SelectColorTarget(ColorTarget target)
{
_colorTarget = target;

_hairTab.EnableInClassList(USS_COLOR_PICKER_TAB_SELECTED, target == ColorTarget.Hair);
_eyesTab.EnableInClassList(USS_COLOR_PICKER_TAB_SELECTED, target == ColorTarget.Eyes);
_skinTab.EnableInClassList(USS_COLOR_PICKER_TAB_SELECTED, target == ColorTarget.Skin);

var color = GetPickerColor(target);
Color.RGBToHSV(color, out var h, out var s, out var v);

_hueSlider.SetValueWithoutNotify(h * 360f);
_saturationSlider.SetValueWithoutNotify(s * 100f);
_valueSlider.SetValueWithoutNotify(v * 100f);

UpdateSliderGradients();

_colorSwatch.style.backgroundColor = color;
}

private void OnColorSliderChanged()
{
var color = Color.HSVToRGB(_hueSlider.value / 360f, _saturationSlider.value / 100f,
_valueSlider.value / 100f);

UpdateSliderGradients();

_colorSwatch.style.backgroundColor = color;

_pickerColors = new AvatarColors(
_colorTarget == ColorTarget.Eyes ? color : GetPickerColor(ColorTarget.Eyes),
_colorTarget == ColorTarget.Hair ? color : GetPickerColor(ColorTarget.Hair),
_colorTarget == ColorTarget.Skin ? color : GetPickerColor(ColorTarget.Skin));

AvatarColorChanged?.Invoke(_colorTarget, color);
}

private void UpdateSliderGradients()
{
var h = _hueSlider.value / 360f;
var s = _saturationSlider.value / 100f;
var v = _valueSlider.value / 100f;

_saturationSlider.SetGradient(Color.HSVToRGB(h, 0f, v), Color.HSVToRGB(h, 1f, v));
_valueSlider.SetGradient(Color.black, Color.HSVToRGB(h, s, 1f));
}

private Color GetPickerColor(ColorTarget target)
{
if (_pickerColors == null) return Color.white;

return target switch
{
ColorTarget.Hair => _pickerColors.Hair,
ColorTarget.Eyes => _pickerColors.Eyes,
ColorTarget.Skin => _pickerColors.Skin,
_ => throw new ArgumentOutOfRangeException(nameof(target), target, null)
};
}

private void OnAvatarButtonClicked()
{
if (_switcherState == SwitcherState.WearableLocked) return;
Expand Down Expand Up @@ -297,5 +423,12 @@ public enum SwitcherState
Avatar,
WearableLocked
}

public enum ColorTarget
{
Hair,
Eyes,
Skin
}
}
}
78 changes: 78 additions & 0 deletions Assets/Scripts/UI/Elements/GradientSliderElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using UnityEngine;
using UnityEngine.UIElements;

namespace UI.Elements
{
/// <summary>
/// A slider whose tracker is painted with a horizontal gradient built from arbitrary color stops.
/// </summary>
[UxmlElement]
public partial class GradientSliderElement : Slider
{
private const string USS_BLOCK = "gradient-slider";
private const string USS_GRADIENT = USS_BLOCK + "__gradient";
private const int HUE_STOPS = 13;

private readonly VisualElement _gradient;
private Color[] _stops;

public GradientSliderElement()
{
AddToClassList(USS_BLOCK);

var tracker = this.Q(className: trackerUssClassName);
_gradient = new VisualElement { name = "gradient", pickingMode = PickingMode.Ignore };
_gradient.AddToClassList(USS_GRADIENT);
_gradient.generateVisualContent += OnGenerateGradient;
tracker.Add(_gradient);
}

public void SetGradient(params Color[] stops)
{
_stops = stops;
_gradient.MarkDirtyRepaint();
}

public void SetHueGradient()
{
var stops = new Color[HUE_STOPS];
for (var i = 0; i < HUE_STOPS; i++)
{
stops[i] = Color.HSVToRGB(i / (float)(HUE_STOPS - 1), 1f, 1f);
}

SetGradient(stops);
}

private void OnGenerateGradient(MeshGenerationContext mgc)
{
var rect = _gradient.contentRect;
if (_stops == null || _stops.Length < 2 || rect.width <= 0f || rect.height <= 0f) return;

var segments = _stops.Length - 1;
var mesh = mgc.Allocate((segments + 1) * 2, segments * 6);

for (var i = 0; i <= segments; i++)
{
var x = rect.xMin + rect.width * i / segments;
Color32 tint = _stops[i];
mesh.SetNextVertex(new Vertex { position = new Vector3(x, rect.yMin, Vertex.nearZ), tint = tint });
mesh.SetNextVertex(new Vertex { position = new Vector3(x, rect.yMax, Vertex.nearZ), tint = tint });
}

for (var i = 0; i < segments; i++)
{
var tl = (ushort)(i * 2);
var bl = (ushort)(i * 2 + 1);
var tr = (ushort)(i * 2 + 2);
var br = (ushort)(i * 2 + 3);
mesh.SetNextIndex(tl);
mesh.SetNextIndex(tr);
mesh.SetNextIndex(br);
mesh.SetNextIndex(tl);
mesh.SetNextIndex(br);
mesh.SetNextIndex(bl);
}
}
}
}
2 changes: 2 additions & 0 deletions Assets/Scripts/UI/Elements/GradientSliderElement.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading