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

CsCamera initial #531

Merged
merged 2 commits into from
Nov 13, 2023
Merged

CsCamera initial #531

merged 2 commits into from
Nov 13, 2023

Conversation

thokkat
Copy link
Contributor

@thokkat thokkat commented Nov 2, 2023

Camera position and target point the camera is looking at move on a trajectory which is represented by a spline. According to some doc in G1 modkit they're of Kochanek-Bartels type. The movement speed along those curves is defined by the attribute motion_type which itself is set for every point that is used to construct the splines. Those points together with other attributes that define the camera trajectory are called Keyframes.

Based on the motion_type of all points a speed curve is constructed. I used a hermite spline which is fully defined by the speed and the distance traveled at each point (see below for a comparison between vanilla and OG). I only considered type slow for start and end point because other types/points aren't used in Gothic (except default smooth) and behavior in testing was partially buggy e.g. fast behaved like step. Other types can be added later in case there's some scene that uses those.

For the position spline every frame we start with a parameter argument t which reflects the current position. The speed curve delivers a distance to move which results from the next frame time. Then a binary search is used to get the new argument tNew which matches the right distance. The length of the spline between these two points is computed numerically. In the end position is updated and applied to camera position. Spline for the target position is handled in the same way and used to compute camera spin.

I tested the produced values against a library implementation to confirm the spline is correctly implemented. But there's still some derivation to the vanilla spline.

A camera Event can be ended by time or by trigger. According to doc duration should be zero in those case but actually it's not. This made it necessary to use a variable to track the activity state of each event. Test case was the lever room for the gates in Irdorath at the switch riddle location. There's also a bug that lower and upper gate camera sequence are played at the same time, so a counter for total active events has been introduced.

For a quick test there's marvin command ztoggle timedemo available for a trip around Khorinis City. If someone has a scene that doesn't work or has wrong behavior feel free to share so I can look into it.

Some comparisons between OpenGothic and vanilla for timedemo behavior:

Start and endpoint have type slow, others smooth

  • Blue is the measured speed for vanilla camera.
  • Orange is an average of erratic blue curve.
  • Green is OpenGothic spline.
  • Red is measured OpenGothic speed, vertical lines are logging artifacts.

Green curve is covered by red one.

normal

Approximation is ok. Noticeable is the drop of the orange curve shortly before 25s. This was visible in all tests. Unclear what's happening there.

Zoomed in:
normal_zoomed

Image is from an older implementation but current shouldn't look different.


Now all points smooth. OpoenGothic spline would assume average speed, a horizontal line at 2000.
all_smooth
Sometimes straight line, sometimes sine like.


Start slow, others smooth
slow_smooth
Here it looks like it should be a straight line with increasing speed from left to right.


Now mirrored. End slow, others smooth
smooth_slow_correct

Looks identical to all smooth instead of mirrored to the previous image. I will retest this to make sure it isn't a copy paste issue.
Edit: new image, looks reasonable now. Speed at start seems to be doubled average. Resembles a straight decreasing line but pretty erratic micro movements. The oscillations were clearly noticeable ingame. My chosen approach seems to be wrong though.

game/world/triggers/cscamera.cpp Outdated Show resolved Hide resolved
game/world/triggers/cscamera.cpp Outdated Show resolved Hide resolved
game/world/triggers/cscamera.h Outdated Show resolved Hide resolved
game/world/triggers/cscamera.h Outdated Show resolved Hide resolved
game/world/triggers/cscamera.h Outdated Show resolved Hide resolved
auto position(CamSpline& cs) -> Tempest::Vec3;
auto spin(const Tempest::Vec3& d) -> Tempest::PointF;

static inline uint8_t activeEvents;
Copy link
Owner

Choose a reason for hiding this comment

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

activeEvents can be placed in WorldObjects
Also does it make sense to allow more than one CsCamera at a time? Any idea on how vanilla react to it?

Also, no need to make it inline - regular static variable is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also does it make sense to allow more than one CsCamera at a time?

There is a bug that makes two occur at the same time

Trigger: EVT_LEFT_UP_CAMERA_01
Trigger: EVT_LEFT_DOWN_CAMERA_01
Untrigger: EVT_LEFT_UP_CAMERA_01
Untrigger: EVT_LEFT_DOWN_CAMERA_01

In vanilla the up event is triggered first, the down event if the levers are used a second time at Irdorath switch riddle location. The variable is only used to guard against it.

Copy link
Owner

Choose a reason for hiding this comment

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

If this suppose to be like "fist-in" scheme, than having a pointer to active CsCamera can be more convinient:

CsCamera* WorldObjects::currentCs = nullptr;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also does it make sense to allow more than one CsCamera at a time? Any idea on how vanilla react to it?

I tested a bit and during timedemo portal opening sequence can be triggered. Timedemo is not continued after finish. This would suggest a new cscamera disables the previous.

If this suppose to be like "fist-in" scheme,

Up event is always first and zSpy shows only one OnTrigger() event in vanilla. There must be something else before that decides which event to take.

a pointer to active CsCamera can be more convinient:

Makes sense with the above observation. Should this be accessed by getter and setter methods through World ?

Copy link
Owner

Choose a reason for hiding this comment

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

Should this be accessed by getter and setter methods through World ?

Yep, that is a good option; also in general - no need to ask such minor details ;)

game/world/triggers/cscamera.cpp Show resolved Hide resolved

const float step = 0.005f;
uB = t + step;
while(d>s->arcLength(t,uB))
Copy link
Owner

Choose a reason for hiding this comment

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

Maybe I've missed something in PR description, but why this part is so complicated?
For spline, code samples here:
https://www.geometrictools.com/Documentation/KBSplines.pdf
and here:
https://github.com/astrowander/tcbspline-gui/blob/ab191b1a785dadfaa8c3577fb4660c6dbff87f88/tsbspline.cpp#L85
are way more simple; also there should not be an issue converting time to position on spline (TSBSpline::interpolate should fit?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but why this part is so complicated?

Equidistant input doesn't mean the arc length is equal between those points. Speed would be everything but smooth.
timedemo-noarcLength
But vanilla has same oscillations so they probably did it that way.

game/world/triggers/cscamera.cpp Outdated Show resolved Hide resolved
@Try
Copy link
Owner

Try commented Nov 4, 2023

Hi, @thokkat and thanks for PR!

Would be really nice to finally have timedemo option :)
Code related to interpolation look a quite hairy; is it possible to have a library based solution? Or maybe refactor spline interpolation related stuff into dedicated class ?

@thokkat
Copy link
Contributor Author

thokkat commented Nov 4, 2023

I would refactor it out then. A library would need adaption anyway to accommodate the speed movement types and without integration it's relatively simple. Where should the class file be placed?

Copy link
Owner

@Try Try left a comment

Choose a reason for hiding this comment

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

Where should the class file be placed?

Can be utils/; private class CsCamera::KbSpline is also fine

auto position(CamSpline& cs) -> Tempest::Vec3;
auto spin(const Tempest::Vec3& d) -> Tempest::PointF;

static inline uint8_t activeEvents;
Copy link
Owner

Choose a reason for hiding this comment

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

If this suppose to be like "fist-in" scheme, than having a pointer to active CsCamera can be more convinient:

CsCamera* WorldObjects::currentCs = nullptr;

@thokkat
Copy link
Contributor Author

thokkat commented Nov 8, 2023

Addressed code review and removed movement base on arc length. Determination of next camera position is now only based on time as input which itself is rescaled based on keyframe motion types. I removed the tension/continuity/bias parameters because implementation was not correct. Non default values aren't in use, so I opted for removal instead of fixing. Like the movement types this can be revisited in case it's needed.

@Try Try merged commit f04671a into Try:master Nov 13, 2023
@Try
Copy link
Owner

Try commented Nov 13, 2023

Merged, thanks!

One idea, that I still have on code-style is to move add camera.isCutscene() function, given that CS usually used in conjunction with camera - will test it outside on code review.

@thokkat thokkat deleted the CutsceneCamera branch December 30, 2023 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants