microsoft / MixedReality-UXTools-Unreal

UX tools and components for developing Mixed Reality applications in UE4.
https://microsoft.github.io/MixedReality-UXTools-Unreal/
MIT License
316 stars 86 forks source link

UxtScaleAxisConstraint feature request #33

Open zotoli opened 3 years ago

zotoli commented 3 years ago

I couldn't find a way to constrain the scale axis while manipulating actors. So I made a new actor component following the style of the other constraints such as UxtRotationAxisConstraint. I am wondering if there is a built in way to accomplish this if not it would be great to see this included in the upcoming releases for organization sake. Also hope to see you enable PRs in the future. Thanks!

Here is the component for your reference:

UxtScaleAxisConstraint.h:

#include "CoreMinimal.h"

#include "Interactions/Constraints/UxtTransformConstraint.h"
#include "Interactions/UxtManipulationFlags.h"

#include "UxtScaleAxisConstraint.generated.h"

/**
 * Component for limiting the scale axes for Manipulator
 *
 * Usage:
 * Attach to actor that the constraint should be applied to.
 */
UCLASS(ClassGroup = "UXTools", meta = (BlueprintSpawnableComponent))
class AUGRE_API UUxtScaleAxisConstraint : public UUxtTransformConstraint
{
    GENERATED_BODY()

public:
    virtual EUxtTransformMode GetConstraintType() const;
    virtual void ApplyConstraint(FTransform& Transform) const;
    virtual void Initialize(const FTransform& WorldPose);

public:
    /** Defines the axis the scale constraint should be applied to. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Uxt Constraint|Scale Axis", meta = (Bitmask, BitmaskEnum = EUxtAxisFlags))
    int32 ConstraintOnScale = 0;

private:
    FVector OriginalScale;
};

UxtScaleAxisConstraint.cpp:

#include "UxtScaleAxisConstraint.h"

EUxtTransformMode UUxtScaleAxisConstraint::GetConstraintType() const
{
    return EUxtTransformMode::Scaling;
}

void UUxtScaleAxisConstraint::ApplyConstraint(FTransform& Transform) const
{

    FVector ConstrainedScale = Transform.GetScale3D();

    if (ConstraintOnScale & static_cast<int32>(EUxtAxisFlags::X))
    {
        ConstrainedScale.X = OriginalScale.X;
    }
    if (ConstraintOnScale & static_cast<int32>(EUxtAxisFlags::Y))
    {
        ConstrainedScale.Y = OriginalScale.Y;
    }
    if (ConstraintOnScale & static_cast<int32>(EUxtAxisFlags::Z))
    {
        ConstrainedScale.Z = OriginalScale.Z;
    }

    Transform.SetScale3D(ConstrainedScale);

}

void UUxtScaleAxisConstraint::Initialize(const FTransform& WorldPose)
{
    OriginalScale = WorldPose.GetScale3D();
}
luis-valverde-ms commented 3 years ago

Hi @zotoli . You can constraint the scale directly in manipulators in UXT 0.12.0. Does that work for you?

image

zotoli commented 3 years ago

Hi @luis-valverde-ms, thanks for your reply. The option you mentioned clamps the overall scaling but doesn't limit on which axes the scaling is performed on as far as I understand. What I am looking for was to limit the scaling to one axis.

For example lets say you have a geometric shape like a cylinder that you would want adjust to height without manipulating the radius. With the example I provided you could set the scaling to be only performed in the axes you would like. I don't think the current scale limiter has that functionality or I wasn't able to see it.

luis-valverde-ms commented 3 years ago

I see what you mean @zotoli . Your solution looks great for that but I'd like to try set that up in the manipulator directly instead. We setup the min/max scale limits directly in the manipulator because we want them to be applied by default and don't want to create an additional component each time you create a manipulator one.

I'm thinking that we could have min and max scale vectors in the manipulator instead of just a scalar. That way you can disable scaling in a given axis by setting the min and max values of the corresponding component to the same value. What do you think?

zotoli commented 3 years ago

That sounds like a good idea. It certainly addresses the scale axis selection that I initially intended this for. However clamping the scale to a vector min/max might have some unintended results. I wonder how intuitive it would be to use since the scale ratio between the axes can change if one of the axes reaches the limit earlier than the others.

I wonder if its a good idea / worth the effort to include some sort of individual axis manipulation capability to further generalize the scale manipulation. Maybe if there was an option to only scale in one axis and we select that axis by finding the most parallel one to the line connecting the grab points.

luis-valverde-ms commented 3 years ago

I wonder how intuitive it would be to use since the scale ratio between the axes can change if one of the axes reaches the limit earlier than the others

There's a way to control that when using a bounds control component via the UniformScaling property. Scaling via the generic manipulator is always uniform (e.g. along all axes) because we thought that restricting the scaling axes without affordances to give a visual cue may be unintuitive for users. Do you find it works fine in your case, i.e. users understand that the object is meant to be scaled only along a given axis?

zotoli commented 3 years ago

Well If I'm honest we only had a few people interact with the objects so far so I can't really comment on the intuitiveness of multiple axis scaling. If its only one axis like in my case then its pretty clear what the object is supposed to do. I think the bounds control would be a better fit for the general user.

luis-valverde-ms commented 3 years ago

I've added a task to look into this to our backlog. We should have something in the next few months, definitely for the next release. Are you happy with your local solution for the time being? If you experiment more and tweak it let us know, whatever you find useful some other people will find too.