Skip to content

Commit cb74b70

Browse files
authored
Merge branch 'dev' into zachr/261/multiplayer
2 parents d852628 + 7a6256e commit cb74b70

1 file changed

Lines changed: 116 additions & 45 deletions

File tree

docs/JOLT_FUNCTIONS_OWNERSHIP_INVARIANTS.md

Lines changed: 116 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ This document lists every Jolt Physics function used in `fission/src/` (producti
55
Jolt memory management possible without reading the Jolt source for every call.
66

77
Fission uses the WebAssembly port [`@synthesis.adsk/jolt-physics`](https://www.npmjs.com/package/@synthesis.adsk/jolt-physics)
8-
(a fork of JoltPhysics.js). Ownership semantics are therefore governed by the Emscripten WebIDL
9-
binding, defined in `jolt/JoltJS.idl` and `jolt/JoltJS.h`, layered on top of Jolt's C++ memory model.
10-
All invariants below were derived from those two files.
8+
(a fork of JoltPhysics.js). Ownership semantics are governed by the Emscripten WebIDL binding,
9+
defined in `jolt/JoltJS.idl` and `jolt/JoltJS.h`, layered on top of Jolt's C++ memory model. The IDL
10+
is compiled by `webidl_binder.py` (at build time) into `glue.cpp` (one file per build config, under
11+
`jolt/Build/<Config>/<ST|MT>/`), which implements each binding's argument- and return-passing as
12+
concrete C++: heap allocation (`new T(...)`), a function-local `static T` scratch, an in-place
13+
mutation of the receiver, or a plain reference into existing state. The categories and per-function
14+
invariants below describe what each generated binding actually does.
1115

1216
---
1317

@@ -63,32 +67,66 @@ Plain (non-`RefTarget`) value types (`Vec3`, `RVec3`, `Quat`, `Mat44`, `Float3`,
6367
- **`INTERNAL_REF`** — the return value is a reference/handle into state owned by another Jolt
6468
object (the parent body, the physics system, a result struct, …). It is valid only while that
6569
owner lives, and the caller **must not** `destroy()` it. In `JoltJS.idl` these are bare interface
66-
pointers or `[Ref]` / `[Const, Ref]` returns.
67-
- **`COPY`** — the return value is a freshly allocated heap object the caller **owns and must
68-
`destroy()`** when done. In `JoltJS.idl` these are `[Value]` returns. Constructors (`new JOLT.X`)
69-
and `*Settings.Create()` also produce caller-owned objects and are treated as `COPY` here.
70+
pointers or `[Ref]` / `[Const, Ref]` returns, implemented in `glue.cpp` as `return self->Getter();`
71+
or `return &self->member;` — the address of something that already exists independent of the call.
72+
- **`COPY`** — the return value is a freshly allocated heap object (`glue.cpp` does `return new
73+
T(...)`) that the caller **owns and must `destroy()`** exactly once. This is **only** true for
74+
constructors (`new JOLT.X(...)`) and for `<TwoBody>ConstraintSettings.Create(body1, body2)` (which
75+
allocates a fresh refcounted `Constraint`).
76+
- **`STATIC_ALIAS`** — the return value is the address of a **function-local `static` C++ variable**
77+
(`glue.cpp`: `static T temp; return (temp = self->Method(), &temp);`), not a heap allocation. The
78+
caller **must never `destroy()` it** — it was never `malloc`/`new`'d, so `JOLT.destroy()` on it is a
79+
bad-free. It is also **invalidated by the next call to that exact same bound function**, anywhere in
80+
the program — that specific `static` is overwritten in place, not reallocated, so holding a
81+
reference to it across another call to the same accessor silently returns stale/wrong data instead
82+
of crashing. This applies to essentially every non-constructor `[Value]`-returning getter, math
83+
operator, and static factory function in the binding: every vector/quaternion/matrix getter
84+
(`GetPosition`, `GetLinearVelocity`, `GetWorldTransform`, `GetCenterOfMass`, `GetTranslation`,
85+
`GetQuaternion`, …), every value-producing math operator (`Normalized`,
86+
`AddVec3`/`SubVec3`/`MulVec3`/`DivVec3`, `MulFloat`/`DivFloat`, …), static factories (`Vec3.sZero`,
87+
`Quat.sIdentity`, `Quat.sRotation`, `AABox.sBiggest`, …), and `ShapeSettings.Create()`. Snapshot the
88+
data you need (read components via `GetX()`/`GetY()`/`GetZ()`, or copy into a `THREE.js` object)
89+
before making any other call that returns the same C++ type from the same function.
90+
- **`ALIASES_THIS`** — the return value is the **same object as the receiver** (`glue.cpp`:
91+
`return &(*self += *inV);` — an in-place compound-assignment operator that mutates `self` and
92+
returns a reference to it). The caller **must never `destroy()` it** — doing so double-frees the
93+
receiver, since the "returned" pointer and the receiver's pointer are identical. Applies to
94+
`Vec3`/`RVec3`'s in-place `Add`/`Sub`/`Mul`/`Div` (**not** the `*Vec3`/`*Float`-suffixed siblings,
95+
which are `STATIC_ALIAS` — see the rule of thumb below).
7096
- **`NONE`** — the function returns `void` or a primitive (`number` / `boolean` / enum). Nothing to
7197
free.
7298

7399
### Rules of thumb (from the binding)
74100

75-
1. `[Value] T SomeGetter()` → returns a **COPY**; you must `destroy()` it. This includes every
76-
vector/quaternion/matrix getter (`GetPosition`, `GetLinearVelocity`, `GetCenterOfMass`,
77-
`GetWorldTransform`, …) and every math operator (`Add`, `Sub`, `Mul`, `Div`, `Normalized`, …).
101+
1. Value-returning getters, math operators, and static factory functions are implemented in
102+
`glue.cpp` as a function-local `static T temp; return (temp = ..., &temp);` — i.e.
103+
**`STATIC_ALIAS`**, not a heap copy. Never `destroy()` these, and never hold one across another
104+
call to that same bound function. This includes every vector/quaternion/matrix getter
105+
(`GetPosition`, `GetLinearVelocity`, `GetCenterOfMass`, `GetWorldTransform`, …) and every
106+
*value-producing* math operator (`Normalized`, `AddVec3`, `MulFloat`, …). The **only** genuine
107+
`COPY` returns are constructors (`new JOLT.X(...)`, which `glue.cpp` implements as a real
108+
`return new T(...)`) and `<TwoBody>ConstraintSettings.Create()`.
78109
2. A bare interface-pointer return (`Body`, `Shape`, `BodyInterface`, `MotorSettings`, …) is an
79110
**INTERNAL_REF**; never `destroy()` it.
80111
3. A Jolt heap object passed as an argument that Jolt merely reads (`[Const, Ref]` / `[Ref]`) is
81112
**CLONED** — you keep ownership. A primitive/enum argument is **COPIED**.
82-
4. Contrary to what one might think, arithmetic methods (e.g. `Div`, `Add`, etc.) on `Jolt.Vec3` and `Jolt.RVec3` do not consume the vector nor do they produce a new one. They modify the `this` vector in place and return a reference to it.
83-
5. Annoyingly, the corresponding float arithmetic functions (e.g. `DivFloat`, `AddFloat`, etc.) on the same classes do not consume the vector, but do produce a newly allocated vector.
113+
4. Arithmetic methods `Add`, `Sub`, `Mul`, `Div` on `Jolt.Vec3` and `Jolt.RVec3` do not consume the
114+
vector nor produce a new one — they modify the `this` vector in place
115+
(`glue.cpp`: `return &(*self += *inV);`) and return a reference to `this`. This is `ALIASES_THIS`:
116+
never `destroy()` the return, since it's the same object as the receiver.
117+
5. The corresponding `*Vec3`/`*Float`-suffixed arithmetic methods (`AddVec3`, `DivFloat`, `MulFloat`,
118+
etc.) do **not** consume the operand and do **not** produce a newly allocated vector — `glue.cpp`
119+
implements these the same way as every other math-op getter: a function-local `static T temp`.
120+
They are `STATIC_ALIAS`, not `COPY`. Never `destroy()` their return value.
84121

85122
---
86123

87124
## AABox
88125

89126
- `AABox.sBiggest()` (static)
90127
- Arguments: None
91-
- Returns: `COPY``[Value] AABox`; caller must `destroy()`.
128+
- Returns: `STATIC_ALIAS``glue.cpp`: `static AABox temp; return (temp = AABox::sBiggest(), &temp);`.
129+
Do **not** `destroy()`; invalidated by the next call to `sBiggest()` anywhere in the program.
92130
- `AABox.mMin` / `AABox.mMax` (field read → `Vec3`)
93131
- Reading these fields yields references into the box; treat values pulled out via further
94132
`[Value]` getters (`GetY()`, etc.) per their own rules. The fields themselves: No Ownership Concerns.
@@ -109,16 +147,19 @@ through `BodyInterface`. Never `destroy()` a `Body`.
109147
- Returns: `INTERNAL_REF` — pointer to the body's motion properties. Do not `destroy()`.
110148
- `Body.GetPosition()` / `GetRotation()` / `GetCenterOfMassPosition()`
111149
- Arguments: None
112-
- Returns: `COPY``[Value] RVec3` / `Quat`. Caller must `destroy()`.
150+
- Returns: `STATIC_ALIAS` — each is its own function-local `static RVec3`/`Quat temp` in `glue.cpp`.
151+
Do **not** `destroy()`. Calling `GetPosition()` again (on any body) overwrites the data the
152+
previous `GetPosition()` result pointed to; `GetRotation()` has its own separate static and does
153+
not alias `GetPosition()`'s.
113154
- `Body.GetWorldTransform()` / `GetCenterOfMassTransform()`
114155
- Arguments: None
115-
- Returns: `COPY``[Value] RMat44`. Caller must `destroy()`.
156+
- Returns: `STATIC_ALIAS``static RMat44 temp` per function. Do **not** `destroy()`.
116157
- `Body.GetWorldSpaceBounds()`
117158
- Arguments: None
118-
- Returns: `COPY``[Value] AABox`. Caller must `destroy()`.
159+
- Returns: `STATIC_ALIAS``static AABox temp`. Do **not** `destroy()`.
119160
- `Body.GetLinearVelocity()` / `GetAngularVelocity()` / `GetAccumulatedForce()`
120161
- Arguments: None
121-
- Returns: `COPY``[Value] Vec3`. Caller must `destroy()`.
162+
- Returns: `STATIC_ALIAS``static Vec3 temp` per function. Do **not** `destroy()`.
122163
- `Body.SetLinearVelocity(velocity: Vec3)` / `SetAngularVelocity(velocity: Vec3)`
123164
- Arguments
124165
- `velocity`: CLONED (`[Const, Ref] Vec3`; value copied in, caller frees)
@@ -225,7 +266,7 @@ pattern).
225266
- Returns: `COPY` — reference counted `Shape`; caller owns the handle.
226267
- `BoxShape.GetHalfExtent()`
227268
- Arguments: None
228-
- Returns: `COPY``[Value] Vec3`. Caller must `destroy()`.
269+
- Returns: `STATIC_ALIAS` (`static Vec3 temp`). Do **not** `destroy()`.
229270

230271
## BoxShapeSettings
231272

@@ -409,8 +450,12 @@ Obtained by `JOLT.castObject(constraint, JOLT.HingeConstraint)`; the cast does n
409450
- `columnIndex`: COPIED (number)
410451
- `column`: CLONED (`[Const, Ref] Vec4`; copied in, caller frees — fission destroys it)
411452
- Returns: `NONE`
412-
- `Mat44.GetTranslation()``COPY` (`[Value] Vec3`); `GetQuaternion()``COPY` (`[Value] Quat`);
413-
`Multiply3x3(v: Vec3)``COPY` (`[Value] Vec3`, arg `v` CLONED). Caller must `destroy()` returns.
453+
- `Mat44.GetTranslation()` / `GetQuaternion()` / `Multiply3x3(v: Vec3)`
454+
- Arguments (for `Multiply3x3`): `v`: CLONED (`[Const, Ref] Vec3`; caller frees).
455+
- Returns: `STATIC_ALIAS` — each has its own `static Vec3`/`Quat temp` in `glue.cpp`
456+
(`Multiply3x3`: `static Vec3 temp; return (temp = self->Multiply3x3(*inV), &temp);`). Do **not**
457+
`destroy()` the return; `JOLT.destroy()` on it is a bad-free, since the address was never
458+
`malloc`/`new`'d.
414459

415460
## MeshShapeSettings
416461

@@ -432,7 +477,8 @@ Obtained by `JOLT.castObject(constraint, JOLT.HingeConstraint)`; the cast does n
432477
Obtained from `Body.GetMotionProperties()` (an `INTERNAL_REF`).
433478

434479
- `MotionProperties.GetInverseMass()` — Returns `NONE` (number).
435-
- `MotionProperties.GetInverseInertiaDiagonal()` — Returns `COPY` (`[Value] Vec3`); caller `destroy()`s.
480+
- `MotionProperties.GetInverseInertiaDiagonal()` — Returns `STATIC_ALIAS` (`static Vec3 temp`); do
481+
**not** `destroy()`.
436482

437483
## MotorSettings
438484

@@ -549,24 +595,24 @@ Obtained from `JoltInterface.GetPhysicsSystem()` (an `INTERNAL_REF`). Never `des
549595
- `new Quat(x: number, y: number, z: number, w: number)`
550596
- Arguments: all COPIED (number)
551597
- Returns: `COPY` — caller owns it, must `destroy()`.
552-
- `Quat.sIdentity()` (static) — Returns `COPY` (`[Value] Quat`); caller `destroy()`s.
598+
- `Quat.sIdentity()` (static) — Returns `STATIC_ALIAS` (`static Quat temp`); do **not** `destroy()`.
553599
- `Quat.sRotation(axis: Vec3, angle: number)` (static)
554600
- Arguments
555601
- `axis`: CLONED (`[Const, Ref] Vec3`; caller frees)
556602
- `angle`: COPIED (number)
557-
- Returns: `COPY` (`[Value] Quat`); caller `destroy()`s.
558-
- `Quat.GetEulerAngles()``COPY` (`[Value] Vec3`); caller `destroy()`s.
603+
- Returns: `STATIC_ALIAS` (`static Quat temp`); do **not** `destroy()`.
604+
- `Quat.GetEulerAngles()``STATIC_ALIAS` (`static Vec3 temp`); do **not** `destroy()`.
559605
- `Quat.GetRotationAngle(axis: Vec3)`
560606
- Arguments: `axis`: CLONED. Returns: `NONE` (number).
561607
- `Quat.GetX()` / `GetY()` / `GetZ()` / `GetW()` — Returns `NONE` (number). No Ownership Concerns.
562608

563609
## RMat44
564610

565-
Returned (by `[Value]`) from `Body.GetWorldTransform()` etc. — those returns are `COPY`s the caller
566-
owns.
611+
Returned from `Body.GetWorldTransform()` etc. — those returns are themselves `STATIC_ALIAS`, not
612+
caller-owned (see `Body` above).
567613

568-
- `RMat44.GetTranslation()``COPY` (`[Value] RVec3`); caller `destroy()`s.
569-
- `RMat44.GetQuaternion()``COPY` (`[Value] Quat`); caller `destroy()`s.
614+
- `RMat44.GetTranslation()``STATIC_ALIAS` (`static RVec3 temp`); do **not** `destroy()`.
615+
- `RMat44.GetQuaternion()``STATIC_ALIAS` (`static Quat temp`); do **not** `destroy()`.
570616

571617
## RRayCast
572618

@@ -577,7 +623,7 @@ owns.
577623
- Returns: `COPY` — caller owns it, must `destroy()`.
578624
- `RRayCast.GetPointOnRay(fraction: number)`
579625
- Arguments: `fraction`: COPIED (number)
580-
- Returns: `COPY` (`[Const, Value] RVec3`); caller `destroy()`s.
626+
- Returns: `STATIC_ALIAS` (`static RVec3 temp`); do **not** `destroy()`.
581627

582628
## RayCastResult
583629

@@ -598,10 +644,18 @@ Accessed as `collector.mHit` (an `INTERNAL_REF` inside the collector). Do not `d
598644
- `new RVec3(x: number, y: number, z: number)`
599645
- Arguments: all COPIED (number)
600646
- Returns: `COPY` — caller owns it, must `destroy()`.
601-
- Math methods — `AddRVec3(other: RVec3)`, `SubRVec3(other: RVec3)`, `Sub(other: Vec3|RVec3)`,
602-
`Mul(scalar: number)`, `Div(scalar: number)`, `Normalized()`
603-
- Arguments: an `RVec3`/`Vec3` operand is CLONED (`[Const, Ref]`, caller frees); a scalar is COPIED.
604-
- Returns: `COPY` (`[Value] RVec3`); caller must `destroy()` the result.
647+
- In-place math methods — `Add(other: Vec3)`, `Sub(other: Vec3)`, `Mul(scalar: number)`,
648+
`Div(scalar: number)`
649+
- Arguments: the `Vec3` operand is CLONED (`[Const, Ref]`, caller frees); a scalar is COPIED.
650+
- Returns: `ALIASES_THIS``glue.cpp`: `return &(*self += *inV);` etc. Mutates `self` in place and
651+
returns a reference to `self`, not a new object. Do **not** `destroy()` the return — it's the
652+
same object as the receiver.
653+
- Value-producing math methods — `AddRVec3(other: RVec3)`, `SubRVec3(other: RVec3)`,
654+
`MulRVec3(other: RVec3)`, `DivRVec3(other: RVec3)`, `MulFloat(scalar: number)`,
655+
`DivFloat(scalar: number)`, `Normalized()`
656+
- Arguments: an `RVec3` operand is CLONED (`[Const, Ref]`, caller frees); a scalar is COPIED.
657+
- Returns: `STATIC_ALIAS` — each has its own `static RVec3 temp` in `glue.cpp`. Do **not**
658+
`destroy()`; invalidated by the next call to that same method (on any `RVec3`).
605659
- `RVec3.Dot(other: RVec3)` — arg CLONED; Returns `NONE` (number).
606660
- `RVec3.GetX()` / `GetY()` / `GetZ()` — Returns `NONE` (number). No Ownership Concerns.
607661

@@ -610,9 +664,9 @@ Accessed as `collector.mHit` (an `INTERNAL_REF` inside the collector). Do not `d
610664
Obtained from `Body.GetShape()` / `ShapeResult.Get()` (`INTERNAL_REF`s). Refcounted — do not
611665
`destroy()` an internal reference.
612666

613-
- `Shape.GetCenterOfMass()``COPY` (`[Value] Vec3`); caller `destroy()`s.
614-
- `Shape.GetLocalBounds()``COPY` (`[Value] AABox`); caller `destroy()`s.
615-
- `Shape.GetMassProperties()``COPY` (`[Value] MassProperties`); caller `destroy()`s.
667+
- `Shape.GetCenterOfMass()``STATIC_ALIAS` (`static Vec3 temp`); do **not** `destroy()`.
668+
- `Shape.GetLocalBounds()``STATIC_ALIAS` (`static AABox temp`); do **not** `destroy()`.
669+
- `Shape.GetMassProperties()``STATIC_ALIAS` (`static MassProperties temp`); do **not** `destroy()`.
616670
- `Shape.GetSubType()``NONE` (enum). No Ownership Concerns.
617671

618672
## ShapeFilter
@@ -629,7 +683,8 @@ Obtained from `Body.GetShape()` / `ShapeResult.Get()` (`INTERNAL_REF`s). Refcoun
629683
- `box`: CLONED (`[Const, Ref] AABox`); `centerOfMass`: CLONED (`[Const, Ref] Vec3`);
630684
`rotation`: CLONED (`[Const, Ref] Quat`); `scale`: CLONED (`[Const, Ref] Vec3`)
631685
- (fission passes throwaway `sBiggest()` / `GetCenterOfMass()` / `sIdentity()` results here —
632-
those are themselves `COPY`s that should be `destroy()`ed.)
686+
those are themselves `STATIC_ALIAS`, not `COPY`; they must **not** be `destroy()`ed, only read
687+
from before the next call to that same static factory/getter.)
633688
- Returns: `COPY` — caller owns the helper and must `destroy()` it (fission does).
634689
- `ShapeGetTriangles.GetVerticesData()`
635690
- Arguments: None
@@ -639,7 +694,8 @@ Obtained from `Body.GetShape()` / `ShapeResult.Get()` (`INTERNAL_REF`s). Refcoun
639694

640695
## ShapeResult
641696

642-
Returned by `*Settings.Create()`. It is itself a `COPY` (caller owns it, must `destroy()`).
697+
Returned by `*Settings.Create()`. It is itself `STATIC_ALIAS`, **not** `COPY` — do not `destroy()` it
698+
(see `ShapeSettings.Create()` below).
643699

644700
- `ShapeResult.HasError()` / `.IsValid` — boolean → `NONE`. No Ownership Concerns.
645701
- `ShapeResult.Get()`
@@ -655,9 +711,16 @@ Returned by `*Settings.Create()`. It is itself a `COPY` (caller owns it, must `d
655711

656712
- `ShapeSettings.Create()`
657713
- Arguments: None
658-
- Returns: `COPY``[Value] ShapeResult`. The caller owns the returned `ShapeResult` and must
659-
`destroy()` it; the `Shape` it wraps is refcounted (see `ShapeResult.Get()`). The settings object
660-
itself is unaffected and must be `destroy()`ed separately.
714+
- Returns: `STATIC_ALIAS`, **not** `COPY``glue.cpp`: `static Shape::ShapeResult temp; return
715+
(temp = self->Create(), &temp);`. Do **not** `destroy()` the returned `ShapeResult`. Because the
716+
IDL binder generates one function per *base* interface, this single `static` is shared by
717+
**every** `*ShapeSettings` subtype used here (`BoxShapeSettings`, `MeshShapeSettings`,
718+
`ConvexHullShapeSettings`, `StaticCompoundShapeSettings`, …) — calling `.Create()` on any one of
719+
them overwrites the same slot every other one's `.Create()` result pointed to. Read/consume the
720+
result (`HasError()`, `.Get()`) before calling `.Create()` again on any `*ShapeSettings` object.
721+
The `Shape` it wraps (via `ShapeResult.Get()`) is refcounted and unaffected by this. The settings
722+
object itself (`BoxShapeSettings`, etc.) is a separate, genuinely heap-allocated `COPY` and must
723+
still be `destroy()`ed.
661724

662725
## Constraint creation — `FixedConstraintSettings` / `HingeConstraintSettings` / `SliderConstraintSettings`.`Create`
663726

@@ -726,17 +789,25 @@ Obtained via `JOLT.castObject(...)`.
726789

727790
- `TwoBodyConstraint.GetConstraintToBody1Matrix()`
728791
- Arguments: None
729-
- Returns: `COPY``[Value] Mat44`. Caller must `destroy()`.
792+
- Returns: `STATIC_ALIAS` (`static Mat44 temp`). Do **not** `destroy()`.
730793

731794
## Vec3
732795

733796
- `new Vec3(x?: number, y?: number, z?: number)` (also `new Vec3(float3: Float3)`)
734797
- Arguments: numbers are COPIED; a `Float3` argument is CLONED (`[Const, Ref]`, caller frees).
735798
- Returns: `COPY` — caller owns it, must `destroy()`.
736-
- Math methods — `Add(other: Vec3)`, `Sub(other: Vec3)`, `Mul(scalar: number)`, `Div(scalar: number)`,
737-
`Normalized()`
799+
- In-place math methods — `Add(other: Vec3)`, `Sub(other: Vec3)`, `Mul(scalar: number)`,
800+
`Div(scalar: number)`
738801
- Arguments: a `Vec3` operand is CLONED; a scalar is COPIED.
739-
- Returns: `COPY` (`[Value] Vec3`); caller must `destroy()` the result.
802+
- Returns: `ALIASES_THIS``glue.cpp`: `return &(*self += *inV);` etc. Mutates `self` in place and
803+
returns a reference to `self`. Do **not** `destroy()` the return — same object as the receiver.
804+
- Value-producing math methods — `AddVec3(other: Vec3)`, `SubVec3(other: Vec3)`,
805+
`MulVec3(other: Vec3)`, `DivVec3(other: Vec3)`, `MulFloat(scalar: number)`,
806+
`DivFloat(scalar: number)`, `Normalized()`, `NormalizedOr(zero: Vec3)`,
807+
`GetNormalizedPerpendicular()`
808+
- Arguments: a `Vec3` operand is CLONED; a scalar is COPIED.
809+
- Returns: `STATIC_ALIAS` — each has its own `static Vec3 temp` in `glue.cpp`. Do **not**
810+
`destroy()`; invalidated by the next call to that same method (on any `Vec3`).
740811
- `Vec3.Dot(other: Vec3)` — arg CLONED; Returns `NONE` (number).
741812
- `Vec3.Length()` — Returns `NONE` (number).
742813
- `Vec3.GetX()` / `GetY()` / `GetZ()` — Returns `NONE` (number). No Ownership Concerns.

0 commit comments

Comments
 (0)