Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Curve to allow for domains outside of [0, 1]. #67857

Merged
merged 1 commit into from
Nov 29, 2024

Conversation

anvilfolk
Copy link
Contributor

@anvilfolk anvilfolk commented Oct 24, 2022

This PR allows users to make curves in whatever app/game-specific domain they wish 📈 Previously, curves were restricted to a domain of [0,1]. Implements proposal, closes #5607.

Depends on #78931 and #77622, whose commits have been cherry-picked in this PR. Can rebase/remove those commits when/if they get merged. I can also squash them into this PR if preferred. Let me know 🥰

Recording.2023-07-06.141220.mp4

⚠️ This PR also enforces previously unenforced value ranges for points in the curve ⚠️ I tested this and it should not alter existing curve's points when they are loaded ⚠️ Instead, this happens, then is corrected when limits are changed:

image

Things accounted for

  • Curve editor supports extended curves
  • Preview generates correctly
  • Baking works correctly
  • Presets are set correctly in new domains
  • Duplicated unit curve test cases adapted to a domain of [-100, 100]
  • Documented that other uses of Curve in the engine require unit curves

@anvilfolk anvilfolk requested a review from a team as a code owner October 24, 2022 22:21
@anvilfolk
Copy link
Contributor Author

anvilfolk commented Oct 24, 2022

Compilation is failing due to compiler not knowing whether to use lerp(floats) or lerp(doubles) when real_t is a double. I don't see that warning or error locally. I'm compiling on Visual Studio 2022 with -dev_mode=true, but that's about it. Is there any flag I ought to be using?

I think it also speaks as to whether we should make a decision to use real_t, float or double for these values. Or maybe just not use lerp at all?

@anvilfolk
Copy link
Contributor Author

Fixed all the issues that were showing up in the checks! I'd love to work on some UI/UX work on the editor itself next , but that only makes sense once this PR is mostly settled :) Looking at godotengine/godot-proposals#5579 and godotengine/godot-proposals#3267, for example!

So LMK if there's any issues or feedback <3

scene/resources/curve.h Outdated Show resolved Hide resolved
@anvilfolk anvilfolk changed the title Extend Curve to allow for domains outside of 0-1. Extend Curve to allow for domains outside of [0, 1]. Nov 9, 2022
@anvilfolk anvilfolk marked this pull request as draft February 17, 2023 20:14
@anvilfolk
Copy link
Contributor Author

Converted to draft since I'm fairly certain this isn't the best way to do it. Will work on it once 4.0 is released!

@akien-mga
Copy link
Member

Is this superseded by #74959?

@anvilfolk
Copy link
Contributor Author

It is not! :) It still very much is something I want to do, but I believe I have a better approach and I will work on reimplementing it as time allows. Might be a while still.

@anvilfolk anvilfolk force-pushed the extended-curve branch 4 times, most recently from d380715 to 336f71a Compare July 6, 2023 17:31
@anvilfolk anvilfolk marked this pull request as ready for review July 6, 2023 18:15
@anvilfolk anvilfolk requested a review from a team as a code owner July 6, 2023 18:15
@anvilfolk
Copy link
Contributor Author

anvilfolk commented Jul 6, 2023

Finally fixed and reopened this PR! I updated the original post with information.

Couple of changes:

  • Opted out of having a unit_curve parameter. This means it's less clutter in the inspector.
  • It's been updated to @MewPurPur 's revamp of the curve editor in Overhaul the Curve Editor #74959
  • Depends on a couple of other PRs that facilitate this one, but can be squashed into this one if you want, though they are all relatively different issues.

Most of the lines of code are documentation and tests. The changes themselves are relatively small, especially once you remove the other two cherry-picked commits.

I have ⚠️ not tested anything other than the curve editor in the inspector ⚠️ though apparently curves are used in other places. The documentation changes do reflect the idea that most curves used in the engine need to be unit curves so 🤷 😅 ?

I believe CI is passing, I just seem to have a perpetual single build type that fails?

Copy link
Contributor

@MewPurPur MewPurPur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code that exists LGTM, however, additional changes are needed to make domains play nicely with existing functionality.

The hidden Alt functionality still considers that 0 and 1 are the edges, and so if you use it on a leftmost or rightmost point, it will be forced into that part of the domain.

Snapping doesn't work properly, because it still snaps on the x-axis based on 1.0/snap_divisions.x. When you fix this, remember that you need to do it in two places, once for the dragging logic, and once for the adding a point logic.

Shift for snapping to an axis will also draw the horizontal line only from 0 to 1.

scene/resources/curve.cpp Outdated Show resolved Hide resolved
editor/plugins/curve_editor_plugin.h Show resolved Hide resolved
@anvilfolk anvilfolk requested review from a team as code owners November 1, 2024 18:34
@anvilfolk
Copy link
Contributor Author

anvilfolk commented Nov 1, 2024

Thanks for the reviews! Excited to see where extended curves may be used elsewhere in the engine :)

Let me know if you want me to squash the 3 commits (which include the two other ancillary PRs).

@fire
Copy link
Member

fire commented Nov 2, 2024

We prefer squashed commits. For future prs, if you want to separate them, use different pull requests.

godotengine/godot-proposals#11030

@KoBeWi
Copy link
Member

KoBeWi commented Nov 2, 2024

They are different pull requests already though.

@anvilfolk
Copy link
Contributor Author

It was mostly to separate discussion & ease of reviewing. It sounds like reviewers are OK with them, so I'll go ahead and squash, especially since some of the latest changes are in the latest commit but really should've applied to other PRs.

@anvilfolk
Copy link
Contributor Author

anvilfolk commented Nov 2, 2024

Looks like I messed up the commits, as usual. Will try fixing soon 🤦

-- edit --

Done!

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally, it works as expected. Code looks good to me.

One potential UX concern is that the concept of unit curve isn't enforced in the inspector, so many nodes will expose the domain properties within the Curve resources even though they shouldn't. This adds visual noise to the inspector, but also room for accidental edits.

The best way to resolve this would be to add a new property hint similar to PROPERTY_HINT_COLOR_NO_ALPHA, except that PROPERTY_HINT_RESOURCE_TYPE is already used here to specify Curve as a valid resource type.

Nonetheless, I'll still approve this PR considering it improves Curve's usability for game design use cases, particularly for users who are not writing the scripts that handle the curves in question.

Testing project: test_pr_67857.zip

@anvilfolk
Copy link
Contributor Author

Thank you for the review!

I only found unsatisfactory solutions to that problem, unfortunately, though I agree it exists.

We could make this into an ArbitraryCurve that Curve inherits from. However, Curve would then simply ignore/throw errors at attempted domain changes. So there's useless, confusing methods. It would should keep backwards compatibility this way though.

Having ArbitraryCurve inherit from the current Curve would mean that any place that requires a curve could be assigned an arbitrary one, so it doesn't solve the problem.

The other solution, which is perhaps most viable, is to add a "unit" toggle for curves, which enables domain properties. Every place that uses curves would still need to check & enforce that it is using unit curves though, so it only solves the problem partially.

@ttencate
Copy link
Contributor

ttencate commented Nov 21, 2024

ArbitraryDomainCurve and UnitCurve cannot inherit from each other, because the Liskov substitution principle doesn't apply in either direction:

  • ArbitraryDomainCurve cannot inherit from UnitCurve, because it would let you set an ArbitraryDomainCurve where only a UnitCurve is valid.
  • UnitCurve cannot inherit from ArbitraryDomainCurve, because it doesn't let you change the domain.

So a correct inheritance hierarchy would be (naming aside):

- Resource
  `- AbstractCurve
     |- UnitCurve (what's currently called Curve)
     `- ArbitraryCurve

The shared functionality would be in AbstractCurve, on which min_domain and max_domain are abstract virtual methods. UnitCurve would override these to just return 0 and 1, whereas ArbitraryCurve would override them to read from fields, and also provide set_min_domain and set_max_domain.

If UnitCurve would just be called Curve, this change is almost backwards compatible; the insertion of AbstractCurve into the inheritance chain does change reflection/introspection, and probably also has backwards compatibility problems in GDExtension.

I don't think this discussion warrants holding up the PR further, though. Just wanted to write it down for posterity.

@anvilfolk
Copy link
Contributor Author

anvilfolk commented Nov 21, 2024

Oh, excellent point, hadn't thought of it at all, thanks for bringing it up.

I think it's worth taking time to do well though, because otherwise restructuring to something sensible probably only happens for Godot 5.

So the question is whether it's worth refactoring things in the way you describe. We could keep a unit curve named Curve for backwards compatibility. This should keep all current functionality working (minus stuff that explicitly relies on fixed Curve class hierarchy).

But it also feels annoying to have things named ArbitraryCurve or AnyCurve or something throughout the codebase. It might be easier to refactor code that requires unit curves to ensure that as issues crop up? We can have those systems connect to Curve::domain_changed and throw errors/warnings?

@anvilfolk anvilfolk closed this Nov 21, 2024
@AThousandShips AThousandShips removed this from the 4.x milestone Nov 21, 2024
@anvilfolk anvilfolk reopened this Nov 21, 2024
@anvilfolk
Copy link
Contributor Author

Mistakenly closed because toddler shenanigans 😅

@AThousandShips AThousandShips added this to the 4.x milestone Nov 21, 2024
@Mickeon
Copy link
Contributor

Mickeon commented Nov 21, 2024

The best way to resolve this would be to add a new property hint similar to PROPERTY_HINT_COLOR_NO_ALPHA, except that PROPERTY_HINT_RESOURCE_TYPE is already used here to specify Curve as a valid resource type.

Only tangentially related, but this falls into the idea I've been concocting for a really long time, involving turning hint and hint_string into multiple components (basically a Dictionary of hints). This is very far off in the future, though, so I wouldn't worry about it much. I feel like it's a problem that may solve itself in the future.
At worst, there could be a configuration warning for all affected properties.

@akien-mga akien-mga modified the milestones: 4.x, 4.4 Nov 28, 2024
@akien-mga akien-mga merged commit 5d462ee into godotengine:master Nov 29, 2024
40 checks passed
@akien-mga
Copy link
Member

Thanks! And congrats for your first merged the merge of your first Godot contribution 🎉

@anvilfolk anvilfolk deleted the extended-curve branch November 29, 2024 22:23
@anvilfolk
Copy link
Contributor Author

Oh my goodness! This legitimately feels so nice :) Very excited to see all the regressions from arbitrary curves making their sneaky way around Godot, hehehe 🤣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extend Curve and CurveEditor to allow for a domain beyond [0, 1]