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

2D: CVVS #46

Open
Dreaming381 opened this issue Jul 4, 2024 · 0 comments
Open

2D: CVVS #46

Dreaming381 opened this issue Jul 4, 2024 · 0 comments
Labels
beginner-friendly This task is suitable for those new to ECS open to contributors Want to contribute? This task is available.

Comments

@Dreaming381
Copy link
Owner

2D: CVVS

While 2D is not my personal focus with this framework, I am not opposed to 2D
features being added by the community, and am willing to help guide its
development. Despite my general lack of interest these days, I have a lot of
experience with the domain.

To kick off the exploration of 2D, the first thing to develop is the foundation
of a 2D transform system. In 3D, we have QVVS. But 2D has an equivalent CVSS.
Building out that foundational structure is what this task is all about!

Task is Prerequisite For

  • 2D Transform System
  • 2D Physics
  • 2D Audio
  • 2D Rendering
  • 2D Skeletal Animation

Background

In 2D, there is only a single axis on which things can rotate. Ironically, this
axis is the very axis that is ignored for position and scale.

Because there is only a single rotation axis, it can be represented as a single
angle in radians. But however memory efficient this is, it actually suffers from
a few annoying issues. One issue is that you always have to wrap around angles.
You could force a range of [0, 2π), but this makes small clockwise rotations
appear as large counter-clockwise rotations. The ranges [-π, π) or (-π, π] are
also valid, but you can no longer rely on modulus to reduce an arbitrary value
into this range. But even if you solve both of these problems, there is still
the major issue that transforming any point or vector but an angle requires
trigonometric operations. On the GPU, this isn’t a huge deal because GPUs have
special-function units that do sin() and cos() relatively efficiently. On
the CPU, things tend to be a little more expensive. And that’s not great when
that’s required for transform hierarchies, positioning and orienting raycasts,
spawning things relative to facing directions, or defining relative spaces for
colliders during collision detection. There’s a better way.

If you recall, the angle in radians is the distance traveled counter-clockwise
around the unit circle. If you start at the right-most point on the unit circle,
you can calculate the coordinates of the point you would end up at on the unit
circle if you traveled the angle in radians. The formula as you know is:

x = cos(angle)

y = sin(angle)

Interestingly, these coordinates are also a unit vector from the origin to the
unit circle at the specified angle.

What many people don’t realize is that you can multiply these vectors to
add the angles. But this isn’t a dot product, cross product, nor
component-wise product. This is a complex product.

The basic idea is that you treat the y-axis as the imaginary axis of the complex
plane. And any coordinate or vector becomes a complex number. Then, you simply
multiply the complex numbers. In code:

result.x = a.x * b.x - a.y * b.y;

result.y = a.x * b.y + a.y * b.x;

There’s some amazing properties to this. First off, unlike quaternions, the
order of the multiplication does not matter. Second, if non-unit vectors are
involved, this expression is true:

complexMul(a, b) = a.mag * b.mag * complexMul(a.norm, b.norm)

If you treat a as a unit vector derived from an angle, and b as some
arbitrary vector, you will find that complex multiplication will rotate b
while preserving the magnitude of b. In fact, you can even substitute the
angle-to-vector formula for a, and you would end up with the well-known formula
for transforming an angle:

result.x = x * cos(angle) - y * sin(angle)

result.y = y * cos(angle) + x * sin(angle)

But now that you know the derivation, you may be able to see how you can skip
the trigonometric functions in many cases. And naturally, it is a good idea to
keep a rotation stored as a complex number. Where Q is for quaternion, C is for
complex. Thus, we can derive our CVVS.

Like quaternions, wrap-around happens automatically for complex numbers.
However, you occasionally need to normalize the complex numbers to correct for
floating point errors, just like quaternions. Another similar property is that
you can invert the rotation of a unit complex number by taking the conjugate of
the complex number, which is just negating the imaginary (y) component.

Lastly, a complex number requires 8 bytes. A position in 2D also requires 8
bytes. Stretch requires 8 bytes, and scale requires 4 bytes. Just like with
QVVS, we can fill in 4 bytes with a worldIndex (use-case defined value) for an
even 32 bytes.

Base Requirements

Implement the TransformCvvs type and the static cvvs class. Additionally,
implement utilities for converting between complex rotations and angles, and
converting between TransformCvvs and TransformQvvs which can either assume
an XY plane or an XZ plane.

You may want to add code in MathematicsExpansion to define the complex type
and implement mul() and rotate() methods.

@Dreaming381 Dreaming381 added beginner-friendly This task is suitable for those new to ECS open to contributors Want to contribute? This task is available. labels Jul 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beginner-friendly This task is suitable for those new to ECS open to contributors Want to contribute? This task is available.
Projects
None yet
Development

No branches or pull requests

1 participant