cs686 commited on
Commit
a550c4e
·
verified ·
1 Parent(s): 8ac8679

Deploy Kimodo ZeroGPU motion API

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +68 -0
  2. .gitignore +160 -0
  3. .pre-commit-config.yaml +50 -0
  4. ATTRIBUTIONS.MD +48 -0
  5. CHANGELOG.md +84 -0
  6. CONTRIBUTING.MD +49 -0
  7. Dockerfile +46 -0
  8. LICENSE +201 -0
  9. MANIFEST.in +6 -0
  10. MotionCorrection/.gitignore +91 -0
  11. MotionCorrection/CMakeLists.txt +95 -0
  12. MotionCorrection/MANIFEST.in +4 -0
  13. MotionCorrection/README.md +31 -0
  14. MotionCorrection/python/motion_correction/__init__.py +1 -0
  15. MotionCorrection/python/motion_correction/motion_postprocess.py +102 -0
  16. MotionCorrection/run_test.py +72 -0
  17. MotionCorrection/setup.py +122 -0
  18. MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.cpp +200 -0
  19. MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.h +35 -0
  20. MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.cpp +524 -0
  21. MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.h +77 -0
  22. MotionCorrection/src/cpp/AnimProcessing/Utility.cpp +1197 -0
  23. MotionCorrection/src/cpp/AnimProcessing/Utility.h +54 -0
  24. MotionCorrection/src/cpp/BindingsPython.cpp +364 -0
  25. MotionCorrection/src/cpp/Compiler.h +25 -0
  26. MotionCorrection/src/cpp/Debug.h +13 -0
  27. MotionCorrection/src/cpp/Math/Constants.h +32 -0
  28. MotionCorrection/src/cpp/Math/Matrix.cpp +305 -0
  29. MotionCorrection/src/cpp/Math/Matrix.h +163 -0
  30. MotionCorrection/src/cpp/Math/Matrix.inl +802 -0
  31. MotionCorrection/src/cpp/Math/Quaternion.cpp +24 -0
  32. MotionCorrection/src/cpp/Math/Quaternion.h +144 -0
  33. MotionCorrection/src/cpp/Math/Quaternion.inl +606 -0
  34. MotionCorrection/src/cpp/Math/SIMD.h +100 -0
  35. MotionCorrection/src/cpp/Math/Scalar.h +187 -0
  36. MotionCorrection/src/cpp/Math/Transform.cpp +19 -0
  37. MotionCorrection/src/cpp/Math/Transform.h +213 -0
  38. MotionCorrection/src/cpp/Math/Transform.inl +508 -0
  39. MotionCorrection/src/cpp/Math/Types.cpp +44 -0
  40. MotionCorrection/src/cpp/Math/Types.h +808 -0
  41. MotionCorrection/src/cpp/Math/Vector.cpp +110 -0
  42. MotionCorrection/src/cpp/Math/Vector.h +454 -0
  43. MotionCorrection/src/cpp/Math/Vector.inl +2224 -0
  44. MotionCorrection/src/cpp/Platform.h +51 -0
  45. README.md +271 -6
  46. app.py +172 -0
  47. assets/banner.png +0 -0
  48. assets/demo_screenshot.png +3 -0
  49. assets/gmr_results.gif +3 -0
  50. assets/mujoco_result.gif +3 -0
.gitattributes CHANGED
@@ -33,3 +33,71 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/demo_screenshot.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/gmr_results.gif filter=lfs diff=lfs merge=lfs -text
38
+ assets/mujoco_result.gif filter=lfs diff=lfs merge=lfs -text
39
+ assets/protomotions_results.gif filter=lfs diff=lfs merge=lfs -text
40
+ assets/sonic_kimodo_demo.gif filter=lfs diff=lfs merge=lfs -text
41
+ assets/teaser.gif filter=lfs diff=lfs merge=lfs -text
42
+ docs/source/_static/arch.png filter=lfs diff=lfs merge=lfs -text
43
+ docs/source/_static/constraints.png filter=lfs diff=lfs merge=lfs -text
44
+ docs/source/_static/demo/editing_mode.png filter=lfs diff=lfs merge=lfs -text
45
+ docs/source/_static/demo/viewer.png filter=lfs diff=lfs merge=lfs -text
46
+ docs/source/_static/overview.png filter=lfs diff=lfs merge=lfs -text
47
+ docs/source/_static/skeletons/g1.png filter=lfs diff=lfs merge=lfs -text
48
+ docs/source/_static/skeletons/skeletons.png filter=lfs diff=lfs merge=lfs -text
49
+ docs/source/_static/skeletons/smplx.png filter=lfs diff=lfs merge=lfs -text
50
+ docs/source/_static/skeletons/soma.png filter=lfs diff=lfs merge=lfs -text
51
+ docs/source/_static/skeletons/soma_skels.png filter=lfs diff=lfs merge=lfs -text
52
+ docs/source/_static/smoothed_root.png filter=lfs diff=lfs merge=lfs -text
53
+ kimodo/assets/skeletons/g1skel34/meshes/g1/head_link.STL filter=lfs diff=lfs merge=lfs -text
54
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_ankle_roll_link.STL filter=lfs diff=lfs merge=lfs -text
55
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_index_0_link.STL filter=lfs diff=lfs merge=lfs -text
56
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_index_1_link.STL filter=lfs diff=lfs merge=lfs -text
57
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_middle_0_link.STL filter=lfs diff=lfs merge=lfs -text
58
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_middle_1_link.STL filter=lfs diff=lfs merge=lfs -text
59
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_palm_link.STL filter=lfs diff=lfs merge=lfs -text
60
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_thumb_1_link.STL filter=lfs diff=lfs merge=lfs -text
61
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hand_thumb_2_link.STL filter=lfs diff=lfs merge=lfs -text
62
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hip_pitch_link.STL filter=lfs diff=lfs merge=lfs -text
63
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hip_roll_link.STL filter=lfs diff=lfs merge=lfs -text
64
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_hip_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
65
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_knee_link.STL filter=lfs diff=lfs merge=lfs -text
66
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_rubber_hand.STL filter=lfs diff=lfs merge=lfs -text
67
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_shoulder_pitch_link.STL filter=lfs diff=lfs merge=lfs -text
68
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_shoulder_roll_link.STL filter=lfs diff=lfs merge=lfs -text
69
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_shoulder_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
70
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_wrist_roll_link.STL filter=lfs diff=lfs merge=lfs -text
71
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_wrist_roll_rubber_hand.STL filter=lfs diff=lfs merge=lfs -text
72
+ kimodo/assets/skeletons/g1skel34/meshes/g1/left_wrist_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
73
+ kimodo/assets/skeletons/g1skel34/meshes/g1/logo_link.STL filter=lfs diff=lfs merge=lfs -text
74
+ kimodo/assets/skeletons/g1skel34/meshes/g1/pelvis.STL filter=lfs diff=lfs merge=lfs -text
75
+ kimodo/assets/skeletons/g1skel34/meshes/g1/pelvis_contour_link.STL filter=lfs diff=lfs merge=lfs -text
76
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_ankle_roll_link.STL filter=lfs diff=lfs merge=lfs -text
77
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_index_0_link.STL filter=lfs diff=lfs merge=lfs -text
78
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_index_1_link.STL filter=lfs diff=lfs merge=lfs -text
79
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_middle_0_link.STL filter=lfs diff=lfs merge=lfs -text
80
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_middle_1_link.STL filter=lfs diff=lfs merge=lfs -text
81
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_palm_link.STL filter=lfs diff=lfs merge=lfs -text
82
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_thumb_1_link.STL filter=lfs diff=lfs merge=lfs -text
83
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hand_thumb_2_link.STL filter=lfs diff=lfs merge=lfs -text
84
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hip_pitch_link.STL filter=lfs diff=lfs merge=lfs -text
85
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hip_roll_link.STL filter=lfs diff=lfs merge=lfs -text
86
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_hip_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
87
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_knee_link.STL filter=lfs diff=lfs merge=lfs -text
88
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_rubber_hand.STL filter=lfs diff=lfs merge=lfs -text
89
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_shoulder_pitch_link.STL filter=lfs diff=lfs merge=lfs -text
90
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_shoulder_roll_link.STL filter=lfs diff=lfs merge=lfs -text
91
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_shoulder_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
92
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_wrist_roll_link.STL filter=lfs diff=lfs merge=lfs -text
93
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_wrist_roll_rubber_hand.STL filter=lfs diff=lfs merge=lfs -text
94
+ kimodo/assets/skeletons/g1skel34/meshes/g1/right_wrist_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
95
+ kimodo/assets/skeletons/g1skel34/meshes/g1/torso_constraint_L_link.STL filter=lfs diff=lfs merge=lfs -text
96
+ kimodo/assets/skeletons/g1skel34/meshes/g1/torso_constraint_R_link.STL filter=lfs diff=lfs merge=lfs -text
97
+ kimodo/assets/skeletons/g1skel34/meshes/g1/torso_link.STL filter=lfs diff=lfs merge=lfs -text
98
+ kimodo/assets/skeletons/g1skel34/meshes/g1/torso_link_23dof_rev_1_0.STL filter=lfs diff=lfs merge=lfs -text
99
+ kimodo/assets/skeletons/g1skel34/meshes/g1/torso_link_rev_1_0.STL filter=lfs diff=lfs merge=lfs -text
100
+ kimodo/assets/skeletons/g1skel34/meshes/g1/waist_constraint_L.STL filter=lfs diff=lfs merge=lfs -text
101
+ kimodo/assets/skeletons/g1skel34/meshes/g1/waist_constraint_R.STL filter=lfs diff=lfs merge=lfs -text
102
+ kimodo/assets/skeletons/g1skel34/meshes/g1/waist_support_link.STL filter=lfs diff=lfs merge=lfs -text
103
+ kimodo/assets/skeletons/g1skel34/meshes/g1/waist_yaw_link.STL filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # debugging files
2
+ debug/
3
+ SMPLX_NEUTRAL.npz
4
+
5
+ # Byte-compiled / optimized / DLL files
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+
10
+ # C extensions
11
+ *.so
12
+
13
+ datasets
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ pip-wheel-metadata/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py,cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+
78
+ # PyBuilder
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+
88
+ # pyenv
89
+ .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99
+ __pypackages__/
100
+
101
+ # Celery stuff
102
+ celerybeat-schedule
103
+ celerybeat.pid
104
+
105
+ # SageMath parsed files
106
+ *.sage.py
107
+
108
+ # Environments
109
+ .env
110
+ .venv
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+
134
+ # vscode
135
+ .vscode
136
+ *.code-workspace
137
+ /pyrightconfig.json
138
+ wandb/
139
+
140
+ # others
141
+ out
142
+ tmr_out
143
+ .ruff_cache
144
+ outputs
145
+ /debug
146
+ /batch*.sh
147
+ checkpoints/**/test/*
148
+ nohup.out
149
+
150
+ *.swp
151
+ *.swo
152
+ *.txt~*
153
+ *.un~
154
+ *~
155
+ train_done
156
+ .aider*
157
+ onelogger.err
158
+
159
+ # deploy files
160
+ /helm-library
.pre-commit-config.yaml ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ repos:
2
+ # code formatting
3
+ - repo: https://github.com/astral-sh/ruff-pre-commit
4
+ rev: v0.6.4
5
+ hooks:
6
+ - id: ruff
7
+ name: sort imports with ruff
8
+ args: [--select, I, --fix]
9
+ - id: ruff-format
10
+ name: format with ruff
11
+
12
+ # docstring formatting
13
+ - repo: https://github.com/PyCQA/docformatter
14
+ rev: v1.7.7
15
+ hooks:
16
+ - id: docformatter
17
+ args:
18
+ [
19
+ --in-place,
20
+ --wrap-summaries=100,
21
+ --wrap-descriptions=100,
22
+ --style=sphinx,
23
+ ]
24
+
25
+ # yaml formatting
26
+ - repo: https://github.com/pre-commit/mirrors-prettier
27
+ rev: v3.0.0-alpha.6
28
+ hooks:
29
+ - id: prettier
30
+ types: [yaml]
31
+ exclude: |
32
+ (?x)^(
33
+ environment\.yaml$ |
34
+ \.gitlab-ci\.yml$ |
35
+ \.k8s/.*\.(ya?ml)$
36
+ )
37
+
38
+ - repo: https://github.com/pre-commit/pre-commit-hooks
39
+ rev: v4.0.1
40
+ hooks:
41
+ - id: trailing-whitespace # Trims trailing whitespace.
42
+ - id: end-of-file-fixer # Makes sure files end in a newline and only a newline.
43
+ - id: check-yaml # Attempts to load all yaml files to verify syntax.
44
+ exclude: |
45
+ (?x)^(
46
+ \.gitlab-ci\.yml$ |
47
+ \.k8s/.*\.(ya?ml)$
48
+ )
49
+
50
+ exclude: "checkpoints/.*"
ATTRIBUTIONS.MD ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LLM2Vec MIT License https://github.com/McGill-NLP/llm2vec Copyright (c) 2024 McGill NLP
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
20
+
21
+ Unitree mujoco BSD 3-Clause License https://github.com/unitreerobotics/unitree_mujoco/blob/main/LICENSE
22
+ Copyright (c) 2016-2024 HangZhou YuShu TECHNOLOGY CO.,LTD. ("Unitree Robotics")
23
+ All rights reserved.
24
+
25
+ Redistribution and use in source and binary forms, with or without
26
+ modification, are permitted provided that the following conditions are met:
27
+
28
+ * Redistributions of source code must retain the above copyright notice, this
29
+ list of conditions and the following disclaimer.
30
+
31
+ * Redistributions in binary form must reproduce the above copyright notice,
32
+ this list of conditions and the following disclaimer in the documentation
33
+ and/or other materials provided with the distribution.
34
+
35
+ * Neither the name of the copyright holder nor the names of its
36
+ contributors may be used to endorse or promote products derived from
37
+ this software without specific prior written permission.
38
+
39
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
40
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
43
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
45
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
46
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
47
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CHANGELOG.md ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [2026-05-03]
8
+
9
+ ### Fixed
10
+ - `benchmark/parse_folder.py` now averages each metric only over the testcases that actually report it. Previously, sparse constraint metrics (`constraint_root2d_acc`, `constraint_root2d_err`, `constraint_root2d_err_p95`, `constraint_fullbody_keyframe`, `constraint_end_effector`) were divided by the total motion count of the (split, category), including testcases of other constraint kinds that did not report the metric. This silently scaled values by `motions_with_metric / total_motions` (e.g. `constraint_root2d_acc` displayed as ~0.57 when the true value was ~0.93). Both the printed table and `summary_rows.json` are affected, including the combined constraints row that merges `constraints_withtext` and `constraints_notext`. Text-following metrics, foot-skate/contact metrics, and TMR metrics are unchanged.
11
+ - Updated Kimodo benchmark results in the documentation with this fix applied.
12
+
13
+ ## [2026-04-24]
14
+
15
+ ### Added
16
+ - Support for `TEXT_ENCODER_DEVICE` environment variable to force LLM2Vec to use the CPU instead of GPU. Setting `TEXT_ENCODER_DEVICE=cpu` reduces VRAM usage to <3 GB with a fairly small speed hit.
17
+ - `--save_example_dir` argument to `kimodo_gen` to save outputs to an example directory that can be directly loaded into `kimodo_demo`
18
+
19
+ ### Fixed
20
+ - Bug in post-processing that was incorrectly making the smoothed root the target for the root in full-body constraints
21
+ - Modified how transitions are handled in multi-prompt generation to improve smoothness
22
+
23
+ ### Removed
24
+ - `share_transition` and `percentage_transition_override` options from python API for multi-prompt generation
25
+
26
+ ## [2026-04-13]
27
+
28
+ ### Added
29
+ - Option `--bvh_standard_tpose` to use standard T-pose for BVH file saved from `generate.py`
30
+ - Option to use standard T-pose for BVH file saved or downloaded from demo
31
+ - Option to input/output BVH files that use standard T-pose with `motion_convert.py`
32
+ - Added BVH file containing the standard Kimodo T-pose to `kimodo/assets/skeletons/somaskel77/somaskel77_standard_tpose.bvh`
33
+ - Updated documentation with these new options
34
+
35
+ ## [2026-04-10]
36
+
37
+ ### Added
38
+ - [Kimodo-SOMA-RP-v1.1](https://huggingface.co/nvidia/Kimodo-SOMA-RP-v1.1) and [Kimodo-SOMA-SEED-v1.1](https://huggingface.co/nvidia/Kimodo-SOMA-SEED-v1.1) models and added support in the codebase. If not specified, the latest version of the models will be used automatically with the demo and CLI.
39
+ - [Kimodo Motion Generation Benchmark](https://huggingface.co/datasets/nvidia/Kimodo-Motion-Gen-Benchmark) for standardized evaluation of motion generation models training on the BONES-SEED dataset.
40
+ - Scripts to construct the full benchmark, generate motions for test cases, and compute evaluation metrics.
41
+ - Documentation explaining the benchmark and how to use the evaluation pipeline.
42
+ - [TMR-SOMA-RP-v1](https://huggingface.co/nvidia/TMR-SOMA-RP-v1) motion-text embedding model to be used for evaluation metrics.
43
+ - Added option to load LLM2Vec text encoder in fp32 precision.
44
+
45
+ ### Fixed
46
+ - Always use batch size 1 with LLM2Vec to avoid unexpected behavior of different embeddings based on batch size.
47
+ - Load LLM2Vec directly onto the GPU, if available.
48
+ - Updated documentation on constraints with more details.
49
+
50
+ ## [2026-04-01]
51
+
52
+ ### Fixed
53
+ - Fix unnecessary text encoder reload when switching between models in the interactive demo (if not using the text encoder server API).
54
+
55
+ ## [2026-03-31]
56
+
57
+ ### Added
58
+ - New `kimodo_convert` CLI tool for converting generated motions between formats (NPZ, BVH, MuJoCo CSV, AMASS NPZ).
59
+ - Support for loading and saving BVH, CSV, and NPZ motion files in the interactive demo.
60
+
61
+ ## [2026-03-27]
62
+
63
+ ### Fixed
64
+ - Bug fix for foot contact visualization in the interactive demo.
65
+ - Patch bug with BVH export for SOMA models.
66
+
67
+ ## [2026-03-19]
68
+
69
+ ### Changed
70
+ - **Breaking:** Model inputs/outputs now use the SOMA 77-joint skeleton (`somaskel77`). This affects saved motion formats and constraint files from previous versions.
71
+
72
+ ### Added
73
+ - Released timeline annotations for the BONES-SEED dataset on HuggingFace.
74
+
75
+ ## [2026-03-16] - Initial Release
76
+
77
+ ### Added
78
+ - Open-source release of Kimodo codebase under Apache-2.0 license.
79
+ - Five model variants: Kimodo-SOMA-RP-v1, Kimodo-G1-RP-v1, Kimodo-SOMA-SEED-v1, Kimodo-G1-SEED-v1, Kimodo-SMPLX-RP-v1.
80
+ - Command-line interface (`kimodo_gen`) for motion generation with text prompts and kinematic constraints.
81
+ - Interactive web-based motion authoring demo (`kimodo_demo`) with timeline editor, constraint tracks, and 3D visualization.
82
+ - Support for multiple output formats: default NPZ, MuJoCo qpos CSV (G1), AMASS NPZ (SMPL-X).
83
+ - Documentation site with quick start guide, installation instructions, CLI reference, and API docs.
84
+ - Compatibility with downstream tools: ProtoMotions (physics-based policy training) and GMR (motion retargeting).
CONTRIBUTING.MD ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to Contribute
2
+
3
+ ## Code Reviews
4
+
5
+ All submissions require review. We use GitHub pull requests for this purpose. Consult
6
+ [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests.
7
+
8
+ ## Signing Your Work
9
+
10
+ * We require that all contributors "sign-off" on their commits. This certifies that the contribution is your original work, or you have rights to submit it under the same license, or a compatible license.
11
+
12
+ * Any contribution which contains commits that are not Signed-Off will not be accepted.
13
+
14
+ * To sign off on a commit you simply use the `--signoff` (or `-s`) option when committing your changes:
15
+ ```bash
16
+ $ git commit -s -m "Add cool feature."
17
+ ```
18
+ This will append the following to your commit message:
19
+ ```
20
+ Signed-off-by: Your Name <your@email.com>
21
+ ```
22
+
23
+ * Full text of the DCO:
24
+
25
+ ```
26
+ Developer Certificate of Origin
27
+ Version 1.1
28
+
29
+ Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
30
+ 1 Letterman Drive
31
+ Suite D4700
32
+ San Francisco, CA, 94129
33
+
34
+ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
35
+ ```
36
+
37
+ ```
38
+ Developer's Certificate of Origin 1.1
39
+
40
+ By making a contribution to this project, I certify that:
41
+
42
+ (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or
43
+
44
+ (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
45
+
46
+ (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
47
+
48
+ (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
49
+ ```
Dockerfile ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM nvcr.io/nvidia/pytorch:24.10-py3
2
+
3
+ # Avoid some interactive prompts + make pip quieter/reproducible-ish
4
+ ENV DEBIAN_FRONTEND=noninteractive \
5
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
6
+ PYTHONDONTWRITEBYTECODE=1 \
7
+ PYTHONUNBUFFERED=1
8
+
9
+ # Where your code will live inside the container
10
+ WORKDIR /workspace
11
+
12
+ # System deps
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ git curl ca-certificates \
15
+ cmake build-essential \
16
+ gosu \
17
+ && rm -rf /var/lib/apt/lists/*
18
+
19
+ # Some base images ship a broken `/usr/local/bin/cmake` shim (from a partial pip install),
20
+ # which shadows `/usr/bin/cmake` and breaks builds that invoke `cmake` (e.g. MotionCorrection).
21
+ # Prefer the system cmake.
22
+ RUN rm -f /usr/local/bin/cmake || true
23
+
24
+ # Install from docker_requirements.txt: kimodo editable (-e .),
25
+ # but MotionCorrection non-editable (./MotionCorrection). The -e . line ensures [project.scripts]
26
+ # from pyproject.toml are installed (kimodo_gen, kimodo_demo, kimodo_textencoder).
27
+ # SKIP_MOTION_CORRECTION_IN_SETUP=1 so setup.py does not bundle motion_correction; it is
28
+ # installed separately from ./MotionCorrection in the requirements file (non-editable).
29
+ COPY docker_requirements.txt /workspace/docker_requirements.txt
30
+ COPY setup.py /workspace/setup.py
31
+ COPY pyproject.toml /workspace/pyproject.toml
32
+ COPY kimodo /workspace/kimodo
33
+ COPY kimodo-viser /workspace/kimodo-viser
34
+ COPY MotionCorrection /workspace/MotionCorrection
35
+
36
+ RUN --mount=type=cache,target=/root/.cache/pip \
37
+ python -m pip install --upgrade pip \
38
+ && SKIP_MOTION_CORRECTION_IN_SETUP=1 python -m pip install -r docker_requirements.txt
39
+
40
+ # Use the docker-entrypoint script, to allow the docker to run as the actual user instead of root
41
+ COPY kimodo/scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
42
+ RUN chmod +x /usr/local/bin/docker-entrypoint
43
+
44
+ # Default command (change to your entrypoint if you have one)
45
+ ENTRYPOINT ["docker-entrypoint"]
46
+ CMD ["bash"]
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
MANIFEST.in ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ include setup.py
2
+ recursive-include kimodo/assets *
3
+ recursive-include MotionCorrection/src *.cpp *.h *.inl
4
+ recursive-include MotionCorrection/python *.py *.dll
5
+ include MotionCorrection/CMakeLists.txt
6
+ include MotionCorrection/test_example.py
MotionCorrection/.gitignore ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ *.whl
11
+ .Python
12
+ develop-eggs/
13
+ .installed.cfg
14
+ pip-log.txt
15
+ pip-delete-this-directory.txt
16
+ .pytest_cache/
17
+ .coverage
18
+ htmlcov/
19
+ .tox/
20
+ .venv
21
+ venv/
22
+ ENV/
23
+ env/
24
+
25
+ # C/C++
26
+ *.o
27
+ *.obj
28
+ *.exe
29
+ *.out
30
+ *.app
31
+ *.dll
32
+ *.dylib
33
+ *.lib
34
+ *.a
35
+ *.la
36
+ *.lo
37
+ *.slo
38
+ *.ko
39
+ *.elf
40
+ *.ilk
41
+ *.map
42
+ *.exp
43
+ *.gch
44
+ *.pch
45
+ *.idb
46
+ *.pdb
47
+ *.mod
48
+ *.smod
49
+ *.lai
50
+
51
+ # CMake
52
+ CMakeCache.txt
53
+ CMakeFiles/
54
+ CMakeScripts/
55
+ cmake_install.cmake
56
+ install_manifest.txt
57
+ CTestTestfile.cmake
58
+ _deps/
59
+ cmake-build-*/
60
+ CMakeUserPresets.json
61
+
62
+ # IDE
63
+ .vscode/
64
+ .idea/
65
+ *.swp
66
+ *.swo
67
+ *~
68
+ .DS_Store
69
+ *.iml
70
+ .project
71
+ .cproject
72
+ .settings/
73
+
74
+ # Visual Studio
75
+ .vs/
76
+ *.user
77
+ *.suo
78
+ *.userosscache
79
+ *.sln.docstates
80
+ *.VC.db
81
+ *.VC.opendb
82
+
83
+ # Build directories
84
+ build/
85
+ Build/
86
+ out/
87
+ dist/
88
+ temp/
89
+
90
+ # Logs
91
+ *.log
MotionCorrection/CMakeLists.txt ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cmake_minimum_required(VERSION 3.15)
2
+ project(motion_correction)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ # Find Python
8
+ find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
9
+
10
+ # Find or fetch pybind11
11
+ find_package(pybind11 CONFIG QUIET)
12
+ if(NOT pybind11_FOUND)
13
+ message(STATUS "pybind11 not found, fetching from GitHub...")
14
+ include(FetchContent)
15
+ FetchContent_Declare(
16
+ pybind11
17
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
18
+ GIT_TAG v2.11.1
19
+ )
20
+ FetchContent_MakeAvailable(pybind11)
21
+ endif()
22
+
23
+ # Find or fetch Eigen
24
+ find_package(Eigen3 3.3 CONFIG QUIET)
25
+ if(NOT Eigen3_FOUND)
26
+ message(STATUS "Eigen3 not found, fetching from GitLab...")
27
+ include(FetchContent)
28
+ FetchContent_Declare(
29
+ Eigen
30
+ GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
31
+ GIT_TAG 3.4.0
32
+ )
33
+ set(EIGEN_BUILD_DOC OFF CACHE BOOL "" FORCE)
34
+ set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
35
+ set(EIGEN_BUILD_PKGCONFIG OFF CACHE BOOL "" FORCE)
36
+ FetchContent_MakeAvailable(Eigen)
37
+ endif()
38
+
39
+ # Source files
40
+ set(MATH_SOURCES
41
+ src/cpp/Math/Matrix.cpp
42
+ src/cpp/Math/Quaternion.cpp
43
+ src/cpp/Math/Transform.cpp
44
+ src/cpp/Math/Types.cpp
45
+ src/cpp/Math/Vector.cpp
46
+ )
47
+
48
+ set(ANIM_SOURCES
49
+ src/cpp/AnimProcessing/InverseKinematics.cpp
50
+ src/cpp/AnimProcessing/TrajectoryCorrector.cpp
51
+ src/cpp/AnimProcessing/Utility.cpp
52
+ )
53
+
54
+ # Create static library for the core functionality
55
+ add_library(motion_correction_cpp_base STATIC ${MATH_SOURCES} ${ANIM_SOURCES})
56
+
57
+ # Enable Position Independent Code (required for linking into shared library)
58
+ set_target_properties(motion_correction_cpp_base PROPERTIES POSITION_INDEPENDENT_CODE ON)
59
+
60
+ target_include_directories(motion_correction_cpp_base PUBLIC
61
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/cpp
62
+ )
63
+
64
+ if(TARGET Eigen3::Eigen)
65
+ target_link_libraries(motion_correction_cpp_base PUBLIC Eigen3::Eigen)
66
+ else()
67
+ target_link_libraries(motion_correction_cpp_base PUBLIC eigen)
68
+ endif()
69
+
70
+ target_compile_definitions(motion_correction_cpp_base PUBLIC EIGEN_MPL2_ONLY)
71
+
72
+ # Compiler-specific settings
73
+ if(MSVC)
74
+ # MSVC-specific flags
75
+ target_compile_options(motion_correction_cpp_base PRIVATE /W4 /arch:AVX)
76
+ else()
77
+ # GCC/Clang flags (also applies to MinGW on Windows)
78
+ # Enable SSE4.1 and AVX instructions for SIMD operations
79
+ target_compile_options(motion_correction_cpp_base PRIVATE -Wall -Wextra -msse4.1 -mavx)
80
+ endif()
81
+
82
+ # Python bindings
83
+ pybind11_add_module(_motion_correction src/cpp/BindingsPython.cpp)
84
+
85
+ target_link_libraries(_motion_correction PRIVATE motion_correction_cpp_base)
86
+
87
+ target_include_directories(_motion_correction PRIVATE
88
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/cpp
89
+ )
90
+
91
+
92
+ # Install the Python module
93
+ install(TARGETS _motion_correction LIBRARY DESTINATION python/motion_correction)
94
+ install(FILES python/motion_correction/__init__.py DESTINATION python/motion_correction)
95
+ install(FILES python/motion_correction/motion_postprocess.py DESTINATION python/motion_correction)
MotionCorrection/MANIFEST.in ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ include CMakeLists.txt
2
+ include test_example.py
3
+ recursive-include src *.cpp *.h *.inl
4
+ recursive-include python *.py *.dll
MotionCorrection/README.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # motion_correction
2
+
3
+ Standalone `correct_motion` implementation packaged as a small C++ motion trajectory correction library with Python bindings.
4
+
5
+ ## Installation Guide
6
+
7
+ ### Prerequisites
8
+
9
+ Ensure you have a C++17 compatible compiler (GCC 7.0+, Clang 5.0+, or MSVC 2017+) and CMake 3.15+. On Windows, install MinGW-w64 or Visual Studio with C++ tools. On Linux, install `build-essential` and `cmake`.
10
+
11
+ This project will download and install additional third-party open source software projects. Review the license terms of these open source projects before use.
12
+
13
+ ### Build & Install
14
+
15
+ #### Standard Installation
16
+ ```bash
17
+ pip install .
18
+ ```
19
+
20
+ #### Development Installation
21
+ ```bash
22
+ pip install -e .
23
+ ```
24
+
25
+ ### Verify Installation
26
+
27
+ ```python
28
+ import motion_correction
29
+ print("Installation successful!")
30
+ ```
31
+ You can also run `python run_test.py` for a simple test.
MotionCorrection/python/motion_correction/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from ._motion_correction import *
MotionCorrection/python/motion_correction/motion_postprocess.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ import os
5
+ import pickle
6
+
7
+ import numpy as np
8
+ import torch
9
+
10
+ import motion_correction
11
+
12
+
13
+ def correct_motion(
14
+ hipTranslations,
15
+ jointRotations,
16
+ contacts,
17
+ hipTranslationsInput,
18
+ rotationsInput,
19
+ constraint_masks,
20
+ contact_threshold,
21
+ root_margin,
22
+ working_rig,
23
+ has_double_ankle_joints=False,
24
+ ):
25
+ joint_names = [x.name for x in working_rig]
26
+ joint_parents = [
27
+ joint_names.index(working_rig[i].parent) if working_rig[i].parent in joint_names else -1
28
+ for i in range(len(working_rig))
29
+ ]
30
+ joint_ref_translations = [list(x.t_pose_translation) for x in working_rig]
31
+ joint_ref_rotations = [list(x.t_pose_rotation) for x in working_rig]
32
+
33
+ left_hand_idx = [i for i in range(len(joint_names)) if working_rig[i].retarget_tag == "LeftHand"]
34
+ if len(left_hand_idx) != 1:
35
+ raise RuntimeError(f"correct_motion: Expected exactly one joint with LeftHand tag")
36
+ left_hand_idx = left_hand_idx[0]
37
+
38
+ right_hand_idx = [i for i in range(len(joint_names)) if working_rig[i].retarget_tag == "RightHand"]
39
+ if len(right_hand_idx) != 1:
40
+ raise RuntimeError(f"correct_motion: Expected exactly one joint with RightHand tag")
41
+ right_hand_idx = right_hand_idx[0]
42
+
43
+ left_foot_idx = [i for i in range(len(joint_names)) if working_rig[i].retarget_tag == "LeftFoot"]
44
+ if len(left_foot_idx) != 1:
45
+ raise RuntimeError(f"correct_motion: Expected exactly one joint with LeftFoot tag")
46
+ left_foot_idx = left_foot_idx[0]
47
+
48
+ right_foot_idx = [i for i in range(len(joint_names)) if working_rig[i].retarget_tag == "RightFoot"]
49
+ if len(right_foot_idx) != 1:
50
+ raise RuntimeError(f"correct_motion: Expected exactly one joint with RightFoot tag")
51
+ right_foot_idx = right_foot_idx[0]
52
+
53
+ end_frame = hipTranslations.shape[1]
54
+
55
+ default_mask = torch.zeros(hipTranslations.shape[1], dtype=torch.float32)
56
+ root_mask = constraint_masks.get("Root", default_mask)
57
+ full_body_mask = constraint_masks.get("FullBody", default_mask)
58
+ left_hand_mask = constraint_masks.get("LeftHand", default_mask)
59
+ right_hand_mask = constraint_masks.get("RightHand", default_mask)
60
+ left_foot_mask = constraint_masks.get("LeftFoot", default_mask)
61
+ right_foot_mask = constraint_masks.get("RightFoot", default_mask)
62
+
63
+ batch_size = hipTranslations.shape[0]
64
+
65
+ for b in range(batch_size):
66
+ hipTranslationsCorrected = hipTranslations[b, :end_frame].detach().cpu().flatten().numpy().astype(np.float32)
67
+ rotationsCorrected = jointRotations[b, :end_frame].detach().cpu().flatten().numpy().astype(np.float32)
68
+
69
+ hipTranslationsInput_flat = hipTranslationsInput.detach().cpu().flatten().numpy().astype(np.float32)
70
+ rotationsInput_flat = rotationsInput.detach().cpu().flatten().numpy().astype(np.float32)
71
+ ctcs = contacts[b].detach().cpu().flatten().numpy().astype(np.float32)
72
+
73
+ motion_correction.correct_motion(
74
+ hipTranslationsCorrected,
75
+ rotationsCorrected,
76
+ hipTranslationsInput_flat,
77
+ rotationsInput_flat,
78
+ full_body_mask,
79
+ left_hand_mask,
80
+ right_hand_mask,
81
+ left_foot_mask,
82
+ right_foot_mask,
83
+ root_mask,
84
+ np.array(ctcs, dtype=np.float32),
85
+ joint_parents,
86
+ joint_ref_translations,
87
+ joint_ref_rotations,
88
+ left_hand_idx,
89
+ right_hand_idx,
90
+ left_foot_idx,
91
+ right_foot_idx,
92
+ contact_threshold,
93
+ root_margin,
94
+ has_double_ankle_joints,
95
+ )
96
+
97
+ hipTranslations[b, :end_frame] = torch.from_numpy(
98
+ hipTranslationsCorrected.reshape(*hipTranslations[b, :end_frame].shape)
99
+ )
100
+ jointRotations[b, :end_frame] = torch.from_numpy(
101
+ rotationsCorrected.reshape(*jointRotations[b, :end_frame].shape)
102
+ )
MotionCorrection/run_test.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import torch
7
+ from motion_correction.motion_postprocess import correct_motion
8
+
9
+
10
+ class Joint:
11
+ def __init__(self, name, parent, t_pose_translation, t_pose_rotation, retarget_tag=""):
12
+ self.name = name
13
+ self.parent = parent
14
+ self.t_pose_translation = t_pose_translation
15
+ self.t_pose_rotation = t_pose_rotation
16
+ self.retarget_tag = retarget_tag
17
+
18
+
19
+ def create_test_rig():
20
+ return [
21
+ Joint("Hips", None, [0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], "Root"),
22
+ Joint("Spine", "Hips", [0.0, 0.1, 0.0], [0.0, 0.0, 0.0, 1.0]),
23
+ Joint("LeftUpLeg", "Hips", [-0.1, -0.05, 0.0], [0.0, 0.0, 0.0, 1.0]),
24
+ Joint("LeftLeg", "LeftUpLeg", [0.0, -0.4, 0.0], [0.0, 0.0, 0.0, 1.0]),
25
+ Joint("LeftFoot", "LeftLeg", [0.0, -0.4, 0.0], [0.0, 0.0, 0.0, 1.0], "LeftFoot"),
26
+ Joint("RightUpLeg", "Hips", [0.1, -0.05, 0.0], [0.0, 0.0, 0.0, 1.0]),
27
+ Joint("RightLeg", "RightUpLeg", [0.0, -0.4, 0.0], [0.0, 0.0, 0.0, 1.0]),
28
+ Joint("RightFoot", "RightLeg", [0.0, -0.4, 0.0], [0.0, 0.0, 0.0, 1.0], "RightFoot"),
29
+ Joint("LeftArm", "Spine", [-0.3, 0.3, 0.0], [0.0, 0.0, 0.0, 1.0]),
30
+ Joint("LeftHand", "LeftArm", [-0.3, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], "LeftHand"),
31
+ Joint("RightArm", "Spine", [0.3, 0.3, 0.0], [0.0, 0.0, 0.0, 1.0]),
32
+ Joint("RightHand", "RightArm", [0.3, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], "RightHand"),
33
+ ]
34
+
35
+
36
+ if __name__ == "__main__":
37
+ # Test data
38
+ batch_size, num_frames, num_joints = 1, 60, 12
39
+
40
+ hipTranslations = torch.randn(batch_size, num_frames, 3)
41
+ jointRotations = torch.randn(batch_size, num_frames, num_joints, 4)
42
+ jointRotations = jointRotations / jointRotations.norm(dim=-1, keepdim=True)
43
+
44
+ contacts = torch.rand(batch_size, num_frames, 4)
45
+ hipTranslationsInput = hipTranslations.clone()
46
+ rotationsInput = jointRotations.clone()
47
+
48
+ constraint_masks = {
49
+ "Root": torch.zeros(num_frames),
50
+ "FullBody": torch.zeros(num_frames),
51
+ "LeftHand": torch.zeros(num_frames),
52
+ "RightHand": torch.zeros(num_frames),
53
+ "LeftFoot": torch.zeros(num_frames),
54
+ "RightFoot": torch.zeros(num_frames),
55
+ }
56
+
57
+ working_rig = create_test_rig()
58
+
59
+ # Run correction
60
+ correct_motion(
61
+ hipTranslations=hipTranslations,
62
+ jointRotations=jointRotations,
63
+ contacts=contacts,
64
+ hipTranslationsInput=hipTranslationsInput,
65
+ rotationsInput=rotationsInput,
66
+ constraint_masks=constraint_masks,
67
+ contact_threshold=0.5,
68
+ root_margin=0.01,
69
+ working_rig=working_rig,
70
+ )
71
+
72
+ print("Test completed successfully")
MotionCorrection/setup.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ """Setup script for correct_motion standalone package."""
6
+
7
+ import os
8
+ import shutil
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from setuptools import Extension, setup
14
+ from setuptools.command.build_ext import build_ext
15
+
16
+
17
+ class CMakeExtension(Extension):
18
+ def __init__(self, name, sourcedir=""):
19
+ Extension.__init__(self, name, sources=[])
20
+ self.sourcedir = os.path.abspath(sourcedir)
21
+
22
+
23
+ class CMakeBuild(build_ext):
24
+ def run(self):
25
+ try:
26
+ subprocess.check_output(["cmake", "--version"])
27
+ except OSError:
28
+ raise RuntimeError("CMake must be installed to build this package")
29
+
30
+ for ext in self.extensions:
31
+ self.build_extension(ext)
32
+
33
+ def build_extension(self, ext):
34
+ # import pdb; pdb.set_trace() # Debug build process
35
+
36
+ extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
37
+ cmake_args = [
38
+ f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}",
39
+ f"-DPYTHON_EXECUTABLE={sys.executable}",
40
+ ]
41
+
42
+ cfg = "Debug" if self.debug else "Release"
43
+ build_args = ["--config", cfg]
44
+
45
+ cmake_args += [f"-DCMAKE_BUILD_TYPE={cfg}"]
46
+
47
+ use_mingw = False
48
+ mingw_bin = None
49
+
50
+ if sys.platform == "win32":
51
+ generator = os.environ.get("CMAKE_GENERATOR", "")
52
+ if generator:
53
+ cmake_args = ["-G", generator] + cmake_args
54
+ if "mingw" in generator.lower():
55
+ use_mingw = True
56
+ else:
57
+ cmake_args += [f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}"]
58
+ else:
59
+ # Try MinGW Makefiles as default on Windows
60
+ try:
61
+ subprocess.check_output(["g++", "--version"], stderr=subprocess.STDOUT)
62
+ use_mingw = True
63
+ cmake_args = ["-G", "MinGW Makefiles"] + cmake_args
64
+ build_args = [] # MinGW Makefiles do not accept --config
65
+ except (OSError, subprocess.CalledProcessError):
66
+ # If g++ is not found, let CMake use its default (Visual Studio)
67
+ cmake_args += [f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}"]
68
+
69
+ if use_mingw:
70
+ gxx_path = shutil.which("g++")
71
+ if gxx_path:
72
+ mingw_bin = Path(gxx_path).parent
73
+ else:
74
+ build_args += ["--", "-j4"]
75
+
76
+ env = os.environ.copy()
77
+ env["CXXFLAGS"] = f'{env.get("CXXFLAGS", "")} -DVERSION_INFO=\\"{self.distribution.get_version()}\\"'
78
+
79
+ if not os.path.exists(self.build_temp):
80
+ os.makedirs(self.build_temp)
81
+
82
+ subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
83
+ subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=self.build_temp)
84
+
85
+ if use_mingw and mingw_bin is not None:
86
+ runtime_libs = [
87
+ "libstdc++-6.dll",
88
+ "libgcc_s_seh-1.dll",
89
+ "libwinpthread-1.dll",
90
+ ]
91
+ extdir_path = Path(extdir)
92
+ extdir_path.mkdir(parents=True, exist_ok=True)
93
+ for lib_name in runtime_libs:
94
+ src_path = mingw_bin / lib_name
95
+ if src_path.exists():
96
+ shutil.copy2(src_path, extdir_path / lib_name)
97
+ else:
98
+ self.announce(
99
+ f"Warning: Expected MinGW runtime DLL '{lib_name}' not found next to g++ (looked in {mingw_bin}). "
100
+ "The built extension may fail to import if the DLL is not on PATH.",
101
+ level=3,
102
+ )
103
+
104
+
105
+ setup(
106
+ name="motion_correction",
107
+ version="1.0.0",
108
+ author="NVIDIA",
109
+ description="Standalone correct_motion function",
110
+ long_description="",
111
+ packages=["motion_correction"],
112
+ package_dir={"": "python"},
113
+ ext_modules=[CMakeExtension("motion_correction._motion_correction")],
114
+ cmdclass={"build_ext": CMakeBuild},
115
+ zip_safe=False,
116
+ python_requires=">=3.8",
117
+ install_requires=[
118
+ "torch>=1.10.0",
119
+ "numpy>=1.19.0",
120
+ # 'cmake' # can install this via pip if the windows system does not have it. But need to run this by yourself before build, not in here.
121
+ ],
122
+ )
MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.cpp ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "InverseKinematics.h"
7
+ #include "Math/Scalar.h"
8
+ #include <iostream>
9
+
10
+
11
+ using namespace IK;
12
+
13
+ namespace
14
+ {
15
+
16
+ float getAngleWithTwoSideVectors(const Math::Vector& vecLeft, const Math::Vector& vecRight)
17
+ {
18
+ auto lNorm = vecLeft.GetNormalized3();
19
+ auto rNorm = vecRight.GetNormalized3();
20
+
21
+ float cosine = lNorm.GetDot3(rNorm);
22
+ float sine = lNorm.Cross3(rNorm).GetLength3();
23
+
24
+ return atan2f(sine, cosine); // in radian
25
+ }
26
+
27
+ float getAngleWithCosineRule (const float lSideLeft, const float lSideRight, const float lSideAcross)
28
+ {
29
+ float val =
30
+ (lSideRight * lSideRight + lSideLeft * lSideLeft - lSideAcross * lSideAcross) /
31
+ (2.0f * lSideLeft * lSideRight);
32
+ val = Math::Clamp(val, -1.0f, 1.0f); // numerical stability. also avoid impossible trangulars
33
+ return acosf(val); // in radian
34
+ }
35
+
36
+ }
37
+
38
+
39
+ void IK::TwoBoneIk(
40
+ Pose& pose,
41
+ const Math::Transform& rootTransform,
42
+ uint32_t cIdx,
43
+ float weight,
44
+ const Math::Vector& target,
45
+ const std::vector<int>& joint_parents_vec,
46
+ const Math::Vector& hintOffset
47
+ )
48
+ {
49
+ weight = Math::Clamp(weight, 0.0f, 1.0f);
50
+ if (!(weight > 0.0f))
51
+ return;
52
+
53
+ // Two bone IK: joints are represented as "a", "b", "c" in the below comments:
54
+ // 1. stage 1, bend joint a and joint b, so that |ac| = |at|, while vec_ac maintain the same direction
55
+ // 2. stage 2, rotate start joint a so that c and t are in the same place
56
+
57
+ // a a a |
58
+ // |\ |\ |\ |
59
+ // | \ | \ | \ |
60
+ // | \ (stage 1 ->) | \ (stage 2 ->) | \ |
61
+ // | b | b | b |
62
+ // | \ | | | / |
63
+ // | \ | | | / |
64
+ // t c t c t(c) |
65
+ // (a is the root joint, b is the middle joint and c is the end joint)
66
+ //
67
+
68
+ int32_t bIdx = joint_parents_vec[cIdx];
69
+ if (bIdx < 0)
70
+ {
71
+ return;
72
+ }
73
+ int32_t aIdx = joint_parents_vec[bIdx];
74
+ if (aIdx < 0)
75
+ {
76
+ return;
77
+ }
78
+
79
+ // Find the parent world transform of joint a:
80
+ Math::Transform aParentWorldTransform = Math::Transform::Identity;
81
+ int32_t idx = joint_parents_vec[aIdx];
82
+ while (idx >= 0)
83
+ {
84
+ aParentWorldTransform = aParentWorldTransform * pose[idx];
85
+ idx = joint_parents_vec[idx];
86
+ }
87
+ aParentWorldTransform = aParentWorldTransform * rootTransform;
88
+
89
+ // Compute world space transforms of a, b and c:
90
+ Math::Transform aWorld = pose[aIdx] * aParentWorldTransform;
91
+ Math::Transform bWorld = pose[bIdx] * aWorld;
92
+ Math::Transform cWorld = pose[cIdx] * bWorld;
93
+
94
+ auto a = aWorld.GetTranslation();
95
+ auto b = bWorld.GetTranslation();
96
+ auto c = cWorld.GetTranslation();
97
+ auto t = Math::Vector::Lerp(c, target, weight);
98
+
99
+ // step 1 (stage 1): extend / contract the joint chain to match the distance
100
+ float eps = 0.0001f; // numerical stability
101
+ float l_ab = (b - a).Length3().GetX();
102
+ float l_bc = (c - b).Length3().GetX();
103
+ float l_at = (a - t).Length3().GetX();
104
+ l_at = Math::Clamp(l_at, eps, (l_ab + l_bc) * 0.999f); // when not reachable, replace with maximum reachable length
105
+
106
+ // get the current angles
107
+ float theta_bac_current = getAngleWithTwoSideVectors(a - b, a - c);
108
+ float theta_abc_current = getAngleWithTwoSideVectors(b - a, b - c);
109
+ // get the desired angles
110
+ if (l_ab < eps || l_bc < eps || l_at < eps)
111
+ {
112
+ return; // the length is too small. rejecting potentially numerically unstable requests.
113
+ }
114
+ float theta_bac_desired = getAngleWithCosineRule(l_ab, l_at, l_bc);
115
+ float theta_abc_desired = getAngleWithCosineRule(l_ab, l_bc, l_at);
116
+
117
+ // in joint[0]'s parent's space
118
+ Math::Vector rotationAxis = Math::Vector::Cross3(c - a, bWorld.TransformPoint(hintOffset) - a);
119
+ float l = rotationAxis.GetLength3();
120
+ if (l == 0)
121
+ {
122
+ rotationAxis = Math::Vector(0,0,1);
123
+ }
124
+ else
125
+ {
126
+ rotationAxis /= l;
127
+ }
128
+
129
+ // get the rotation with axis in the local space of joint a and joint b
130
+ Math::Vector rotationAxisLocalInBSpace = bWorld.GetRotation().RotateVectorInverse(rotationAxis);
131
+ Math::Transform rotateInB(
132
+ Math::Quaternion(rotationAxisLocalInBSpace,
133
+ (theta_abc_desired - theta_abc_current)), Math::Vector::Zero);
134
+
135
+ pose[bIdx] = rotateInB * pose[bIdx];
136
+
137
+ Math::Vector rotationAxisLocalInASpace = aWorld.GetRotation().RotateVectorInverse(rotationAxis);
138
+ Math::Transform rotateInA(
139
+ Math::Quaternion(rotationAxisLocalInASpace,
140
+ (theta_bac_desired - theta_bac_current)), Math::Vector::Zero);
141
+
142
+ pose[aIdx] = rotateInA * pose[aIdx];
143
+
144
+ // recompute a's world space transform as we're going to need it:
145
+ aWorld = pose[aIdx] * aParentWorldTransform;
146
+
147
+ // step 2 (stage 2): rotate joint a so that the target and the end joint c matches
148
+ auto acLocal = aWorld.GetRotation().RotateVectorInverse(
149
+ c - a);
150
+ auto atLocal = aWorld.GetRotation().RotateVectorInverse(
151
+ target - a);
152
+ Math::Transform rotateStageTwo(
153
+ Math::Quaternion::FromRotationBetweenVectors(acLocal, atLocal), Math::Vector::Zero
154
+ );
155
+
156
+ pose[aIdx] = rotateStageTwo * pose[aIdx];
157
+
158
+ }
159
+
160
+ void IK::OneBoneIk(
161
+ Pose& pose,
162
+ const Math::Transform& rootTransform,
163
+ uint32_t bIdx,
164
+ float weight,
165
+ const Math::Vector& target,
166
+ const std::vector<int>& joint_parents_vec
167
+ )
168
+ {
169
+ weight = Math::Clamp(weight, 0.0f, 1.0f);
170
+ if (!(weight > 0.0f))
171
+ return;
172
+
173
+ int32_t aIdx = joint_parents_vec[bIdx];
174
+ if (aIdx < 0)
175
+ {
176
+ return;
177
+ }
178
+
179
+ // Find the parent world transform of joint a:
180
+ Math::Transform aParentWorldTransform = Math::Transform::Identity;
181
+ int32_t idx = joint_parents_vec[aIdx];
182
+ while (idx >= 0)
183
+ {
184
+ aParentWorldTransform = aParentWorldTransform * pose[idx];
185
+ idx = joint_parents_vec[idx];
186
+ }
187
+ aParentWorldTransform = aParentWorldTransform * rootTransform;
188
+
189
+ // Compute world space transforms of a, b and c:
190
+ Math::Transform aWorld = pose[aIdx] * aParentWorldTransform;
191
+ Math::Transform bWorld = pose[bIdx] * aWorld;
192
+
193
+ auto abLocal = aWorld.GetRotation().RotateVectorInverse(
194
+ bWorld.GetTranslation() - aWorld.GetTranslation());
195
+ auto atLocal = aWorld.GetRotation().RotateVectorInverse(
196
+ target - aWorld.GetTranslation());
197
+
198
+ auto deltaRLocal = Math::Quaternion::NLerp(Math::Quaternion::Identity, Math::Quaternion::FromRotationBetweenVectors(abLocal, atLocal), weight);
199
+ pose[aIdx].SetRotation(deltaRLocal * pose[aIdx].GetRotation());
200
+ }
MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.h ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Math/Transform.h"
9
+
10
+ #include <vector>
11
+
12
+ using Pose = std::vector<Math::Transform>;
13
+
14
+ namespace IK {
15
+
16
+ void TwoBoneIk(
17
+ Pose& pose,
18
+ const Math::Transform& rootTransform,
19
+ uint32_t jointIdx,
20
+ float weight,
21
+ const Math::Vector& target,
22
+ const std::vector<int>& joint_parents_vec,
23
+ const Math::Vector& hintOffset = Math::Vector::Zero
24
+ );
25
+
26
+ void OneBoneIk(
27
+ Pose& pose,
28
+ const Math::Transform& rootTransform,
29
+ uint32_t jointIdx,
30
+ float weight,
31
+ const Math::Vector& target,
32
+ const std::vector<int>& joint_parents_vec
33
+ );
34
+
35
+ }
MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.cpp ADDED
@@ -0,0 +1,524 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "TrajectoryCorrector.h"
7
+ #include <iostream>
8
+
9
+ static void removeRows(
10
+ Eigen::SparseMatrix<double>& M,
11
+ Eigen::MatrixXd *v,
12
+ int minCoeffs)
13
+ {
14
+ Eigen::SparseMatrix<double, Eigen::RowMajor> rowMajorMat = M;
15
+ rowMajorMat.makeCompressed(); // Ensure compressed format
16
+
17
+ std::vector<Eigen::Triplet<double>> triplets;
18
+ triplets.reserve(rowMajorMat.nonZeros());
19
+
20
+ int newRow = 0;
21
+ for (int i = 0; i < rowMajorMat.outerSize(); ++i) {
22
+ // Get nonzero count via outerIndexPtr (compressed format only)
23
+ int nnz = rowMajorMat.outerIndexPtr()[i + 1] - rowMajorMat.outerIndexPtr()[i];
24
+
25
+ if (nnz >= minCoeffs) {
26
+ // Iterate through nonzeros in this row
27
+ for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(rowMajorMat, i); it; ++it) {
28
+ triplets.emplace_back(newRow, it.col(), it.value());
29
+ }
30
+ if (v)
31
+ {
32
+ v->row(newRow) = v->row(i);
33
+ }
34
+ newRow++;
35
+ }
36
+ }
37
+
38
+ M = Eigen::SparseMatrix<double>(newRow, M.cols());
39
+ M.setFromTriplets(triplets.begin(), triplets.end());
40
+ if (v)
41
+ {
42
+ v->conservativeResize(newRow, v->cols());
43
+ }
44
+ }
45
+
46
+ static void multVelWeights(
47
+ Eigen::SparseMatrix<double>& V,
48
+ Eigen::MatrixXd* v_rhs,
49
+ const Eigen::VectorXd& velocityWeights
50
+ )
51
+ {
52
+ Eigen::SparseMatrix<double, Eigen::RowMajor> rowMajorMat = V;
53
+ rowMajorMat.makeCompressed(); // Ensure compressed format
54
+
55
+ std::vector<Eigen::Triplet<double>> triplets;
56
+ triplets.reserve(rowMajorMat.nonZeros());
57
+
58
+ for (int i = 0; i < rowMajorMat.outerSize(); ++i) {
59
+ // Iterate through nonzeros in this row
60
+ Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(rowMajorMat, i);
61
+ double vel_weight = velocityWeights[it.col()];
62
+ for(; it; ++it)
63
+ {
64
+ triplets.emplace_back(i, it.col(), it.value() * vel_weight);
65
+ }
66
+ if (v_rhs)
67
+ {
68
+ (*v_rhs).row(i) = (*v_rhs).row(i) * vel_weight;
69
+ }
70
+ }
71
+ }
72
+
73
+
74
+ void TrajectoryCorrector::computeDiffMats(
75
+ Eigen::SparseMatrix<double>& V,
76
+ Eigen::SparseMatrix<double>& A,
77
+ uint32_t N,
78
+ const Eigen::VectorXd& velocityWeights,
79
+ Eigen::MatrixXd* v_rhs,
80
+ Eigen::MatrixXd* a_rhs)
81
+ {
82
+ std::vector<Eigen::Triplet<double>> tripletList;
83
+
84
+ // Identity matrix"
85
+ tripletList.clear();
86
+ Eigen::SparseMatrix<double> I(N, N);
87
+ for (uint32_t i = 0; i < N; ++i)
88
+ {
89
+ tripletList.emplace_back(i, i, 1);
90
+ }
91
+ I.setFromTriplets(tripletList.begin(), tripletList.end());
92
+
93
+ // urr, a time translation operator? Gives you the value on the next frame.
94
+ // Leave the last row blank because that's the end of the timeline.
95
+ tripletList.clear();
96
+ Eigen::SparseMatrix<double> T(N, N);
97
+ Eigen::MatrixXd t_rhs;
98
+ for(uint32_t i = 0; i < N-1; ++i)
99
+ {
100
+ // next frame is
101
+ tripletList.emplace_back(i, i+1, 1.0);
102
+ }
103
+ T.setFromTriplets(tripletList.begin(), tripletList.end());
104
+
105
+ // v = Tx + t_rhs - x;
106
+ // v = (T - I)x + t_rhs;
107
+ V = T - I;
108
+ if (v_rhs)
109
+ {
110
+ *v_rhs = t_rhs;
111
+ }
112
+ removeRows(V, v_rhs, 2);
113
+
114
+ // a = -x + 2 (T x + t_rhs) - (T (T x + t_rhs) + t_rhs)
115
+ // a = (-I + 2 T - T^2) x + t_rhs - T t_rhs
116
+ A = 2 * T - I - T * T;
117
+ if (a_rhs)
118
+ {
119
+ *a_rhs = t_rhs - T * t_rhs;
120
+ }
121
+ removeRows(A, a_rhs, 3);
122
+
123
+ if (velocityWeights.size() > 0)
124
+ {
125
+ multVelWeights(V, v_rhs, velocityWeights);
126
+ }
127
+ }
128
+
129
+ TrajectoryCorrector::TrajectoryCorrector(
130
+ const Eigen::VectorXd& margins,
131
+ float pos_weight,
132
+ float vel_weight,
133
+ float acc_weight,
134
+ const Eigen::VectorXd& velocityWeights,
135
+ uint32_t admm_iters ) :
136
+ m_admm_iters(admm_iters)
137
+ {
138
+
139
+ // This class is used to modify a trajectory to hit specific values at
140
+ // specific frames, while respecting the following soft constraints:
141
+
142
+ // * Preserve the original positions
143
+ // * Preserve the original velocities
144
+ // * Preserve the original accelerations
145
+
146
+ // The weights of these soft constraints are specified in "pos_weight" etc.
147
+
148
+ // This is posed as a minimization problem:
149
+
150
+ // E(x) = pos_weight * |x - x_orig|^2 + vel_weight * |V x - V x_orig| + acc_weight * |A x - A x_orig|
151
+
152
+ // where you minimize E(x) subject to specified values at indices where "mask"
153
+ // is equal to 1. V is a matrix that computes the N-1 velocities between frame n-1 and frame n,
154
+ // and A computes the N-2 accelerations associated with frames n-1, n and n+1.
155
+
156
+ // In addition to this, there are constraints where the trajectory is allowed to
157
+ // deviate from the target points by a maximum margin. The "margins" input to this
158
+ // constructor specifies what type of constraint is active on a particular frame:
159
+
160
+ // margins[0] < 0 ==> unconstrained
161
+ // margins[i] == 0 ==> pinned on this frame
162
+ // margins[i] > 0 ==> can deviate within the margin
163
+
164
+ // I'm solving the optimization problem using ADMM, ie following equations
165
+ // 8,9,10 on this paper:
166
+
167
+ // https://mattoverby.net/files/admm-pd-overby17.pdf
168
+
169
+ uint32_t N = uint32_t(margins.rows());
170
+ for(uint32_t i = 0; i < N; ++i)
171
+ {
172
+ if( margins[i] > 0 )
173
+ {
174
+ m_margin_locs.push_back(i);
175
+ m_margin_vals.push_back(margins[i]);
176
+ }
177
+
178
+ if(margins[i] == 0)
179
+ {
180
+ m_constrained_locs.push_back(i);
181
+ }
182
+ else
183
+ {
184
+ m_unconstrained_locs.push_back(i);
185
+ }
186
+ }
187
+
188
+ Eigen::SparseMatrix<double> V, A;
189
+ computeDiffMats(
190
+ V, A,
191
+ N, velocityWeights
192
+ );
193
+
194
+ // build an identity matrix:
195
+ std::vector<Eigen::Triplet<double>> tripletList;
196
+ Eigen::SparseMatrix<double> I(N, N);
197
+ for (uint32_t i = 0; i < N; ++i)
198
+ {
199
+ tripletList.emplace_back(i, i, 1.0f);
200
+ }
201
+ I.setFromTriplets(tripletList.begin(), tripletList.end());
202
+
203
+ /*
204
+ self.N = (
205
+ self.pos_weight * torch.diag_embed(torch.full_like(interp_mask, 1)) +
206
+ self.vel_weight * torch.matmul(self.V.T, self.V) +
207
+ self.acc_weight * torch.matmul(self.A.T, self.A)
208
+ )
209
+ */
210
+
211
+ m_N = pos_weight * I + vel_weight * (V.transpose() * V) + acc_weight * (A.transpose() * A);
212
+
213
+ double diagMax = 0;
214
+ for (uint32_t i = 0; i < N; ++i)
215
+ {
216
+ diagMax = std::max(m_N.coeff(i,i), diagMax);
217
+ }
218
+ m_admm_stepsize = 0.5f * sqrtf(float(diagMax));
219
+
220
+ /*
221
+ M = (
222
+ self.N +
223
+ self.admm_stepsize * torch.matmul(self.S.T, self.S)
224
+ )
225
+ */
226
+ tripletList.clear();
227
+ Eigen::SparseMatrix<double> M(N, N);
228
+ for( auto i : m_margin_locs)
229
+ {
230
+ tripletList.emplace_back(i, i, m_admm_stepsize);
231
+ }
232
+ M.setFromTriplets(tripletList.begin(), tripletList.end());
233
+ M += m_N;
234
+
235
+ /*
236
+ self.lhsmat = torch.matmul(self.U.T, torch.matmul(self.M, self.U))
237
+ self.lhsmat_inv = torch.inverse(self.lhsmat)
238
+ */
239
+ tripletList.clear();
240
+ Eigen::SparseMatrix<double> S(m_unconstrained_locs.size(), N);
241
+ for (uint32_t i = 0; i < m_unconstrained_locs.size(); ++i)
242
+ {
243
+ uint32_t ifull = m_unconstrained_locs[i];
244
+ tripletList.emplace_back(i, ifull, 1.0f);
245
+ }
246
+ S.setFromTriplets(tripletList.begin(), tripletList.end());
247
+ M = S * M * S.transpose();
248
+
249
+ if(m_unconstrained_locs.size())
250
+ {
251
+ m_system_lu.compute(M);
252
+ }
253
+ }
254
+
255
+
256
+ void TrajectoryCorrector::Interpolate(
257
+ Eigen::MatrixXd& x,
258
+ const Eigen::MatrixXd& observations,
259
+ const Eigen::MatrixXd& ref_positions
260
+ ) const
261
+ {
262
+ if(
263
+ m_constrained_locs.empty() &&
264
+ m_margin_locs.empty()
265
+ )
266
+ {
267
+ x = ref_positions;
268
+ return;
269
+ }
270
+
271
+ uint32_t numCols = uint32_t(x.cols());
272
+ if(m_margin_locs.empty())
273
+ {
274
+ x_update(
275
+ x,
276
+ Eigen::MatrixXd(0, numCols),
277
+ Eigen::MatrixXd(0, numCols),
278
+ ref_positions,
279
+ observations
280
+ );
281
+ }
282
+ else
283
+ {
284
+ x = ref_positions;
285
+ Eigen::MatrixXd z(m_margin_locs.size(), numCols);
286
+ Eigen::MatrixXd z_t(m_margin_locs.size(), numCols);
287
+ Eigen::MatrixXd u(m_margin_locs.size(), numCols);
288
+ for( uint32_t i = 0; i < m_margin_locs.size(); ++i)
289
+ {
290
+ for(uint32_t j = 0; j < numCols; ++j)
291
+ {
292
+ z_t(i, j) = observations(m_margin_locs[i], j);
293
+ z(i, j) = ref_positions(m_margin_locs[i], j);
294
+ u(i, j) =0;
295
+ }
296
+ }
297
+
298
+ for(uint32_t i = 0; i < m_admm_iters; ++i)
299
+ {
300
+ x_update(
301
+ x,
302
+ z,
303
+ u,
304
+ ref_positions,
305
+ observations
306
+ );
307
+ z_update(z, x, z_t, u);
308
+ u_update(u, x, z);
309
+ }
310
+ }
311
+
312
+ }
313
+
314
+ void TrajectoryCorrector::x_update(
315
+ Eigen::MatrixXd &x,
316
+ const Eigen::MatrixXd &z,
317
+ const Eigen::MatrixXd &u,
318
+ const Eigen::MatrixXd &x_t, // reference positions - defines the original shape of the curve that we want to preserve
319
+ const Eigen::MatrixXd &x_o // target positions for constraints
320
+ ) const
321
+ {
322
+
323
+ uint32_t numRows = uint32_t(x.rows());
324
+ uint32_t numCols = uint32_t(x.cols());
325
+
326
+ // Here's what we're minimizing with ADMM:
327
+ // min f(x) + g(z)
328
+ // s.t A x + B z = c
329
+
330
+ // Make these choices so that z = S x:
331
+ // A = S, B = -I, c = 0
332
+ //
333
+ // g(z) = infinity when it's too far away from z_target, zero otherwise
334
+ //
335
+ // f(x) penalizes deviations in position, velocity and acceleration
336
+ // from a reference trajectory:
337
+ //
338
+ // f(x) = 1/2(
339
+ // kx |I x - x_t|^2 +
340
+ // kv |V x - v_t|^2 +
341
+ // kx |A x - a_t|^2
342
+ // )
343
+ //
344
+ // It's also infinite when components of x devaiate from their target
345
+ // values where they're pinned...
346
+
347
+ // Substituting the matrices into the standard admm update rules gives us this:
348
+ // x{n+1} = argmin(f(x) + ρ/2 |S x - z{n} + u{n}|^2)
349
+ // z{n+1} = argmin(g(z) + ρ/2 |S x{n+1} - z + u{n}|^2)
350
+ // u{n+1} = u{n} + (S x{n+1} - z{n+1})
351
+ //
352
+
353
+ // x update:
354
+ //
355
+ // x{n+1} = argmin 1/2 (
356
+ // kx |I x - x_t|^2 +
357
+ // kv |V x - v_t|^2 +
358
+ // ka |A x - a_t|^2 +
359
+ // ρ |S x - d|^2
360
+ // )
361
+ // d = (z{n} - u{n})
362
+
363
+ // Rewrite in a friendlier way:
364
+ // |A x - b|^2 = x^T A^T A x - 2 x^T A^T b + C
365
+ // 1/2 (
366
+ // kx (x^T x - 2 x^T x_t) +
367
+ // kv (x^T V^T V x - 2 x^T V^T v_t) +
368
+ // ka (x^T A^T A x - 2 x^T A^T a_t) +
369
+ // ρ (x^T S^T S x - 2 x^T S^T d)
370
+ // ) + C
371
+ //
372
+ // 1/2 x^T (kx I + kv V^T V + ka A^T A + ρ S^T S) x
373
+ // - x^T (kx x_t + kv V^T v_t + ka A^T a_t + ρ S^T d)
374
+ // + C
375
+ //
376
+ // voila:
377
+ // M = kx I + kv V^T V + ka A^T A + ρ S^T S
378
+ // r = kx x_t + kv V^T v_t + ka A^T a_t + ρ S^T d
379
+ // E = 1/2 x^T M x - x^T r + C
380
+
381
+ /*
382
+ r = (
383
+ torch.matmul(self.N, x_t - x_o_filtered) +
384
+ self.admm_stepsize * torch.matmul(self.S.T, - u + z)
385
+ )
386
+ */
387
+ Eigen::MatrixXd x_diffs(x_t);
388
+ for(auto i : m_constrained_locs)
389
+ {
390
+ for(uint32_t j = 0; j < numCols; ++j)
391
+ {
392
+ x_diffs(i, j) = x_diffs(i,j) - x_o(i,j);
393
+ }
394
+ }
395
+
396
+ Eigen::MatrixXd r = m_N * x_diffs;
397
+
398
+ for(uint32_t i = 0; i < m_margin_locs.size(); ++i)
399
+ {
400
+ uint32_t ifull = m_margin_locs[i];
401
+ for(uint32_t j = 0; j < numCols; ++j)
402
+ {
403
+ r(ifull, j) = r(ifull, j) + m_admm_stepsize * (z(i,j) - u(i,j));
404
+ }
405
+ }
406
+
407
+ // Solve with respect to pin constraints:
408
+ // x = U x_r + x_o
409
+ // E = 1/2 (U x_r + x_o)^T M (U x_r + x_o) - (U x_r + x_o)^T r + C
410
+ // E = 1/2 (x_r^T U^T + x_o^T) M (U x_r + x_o) - (x_r^T U^T + x_o^T) r + C
411
+ // E = 1/2 (x_r^T U^T M (U x_r + x_o) + x_o^T M (U x_r + x_o)) - x_r^T U^T r - x_o^T r + C
412
+ // E = 1/2 (x_r^T U^T M U x_r) + x_r^T U^T (M x_o - r) + C
413
+
414
+ // minimized when x_r solves this equation:
415
+ // U^T M U x_r + U^T (M x_o - r) = 0
416
+ // x_r = (U^T M U)^-1 U^T (r - M x_o)
417
+
418
+ // collapse r down to unconstrained variable set:
419
+ // rhs = torch.matmul(self.U.T, r)
420
+
421
+ uint32_t numRows_reduced = m_unconstrained_locs.size();
422
+ Eigen::MatrixXd r_reduced(numRows_reduced, numCols);
423
+ for(uint32_t i = 0; i < numRows_reduced; ++i)
424
+ {
425
+ uint32_t ifull = m_unconstrained_locs[i];
426
+ for(uint32_t j = 0; j < numCols; ++j)
427
+ {
428
+ r_reduced(i,j) = r(ifull, j);
429
+ }
430
+ }
431
+
432
+ // solve system:
433
+ // x_r = torch.matmul(self.lhsmat_inv, rhs)
434
+ r_reduced.conservativeResize(r_reduced.rows(), r_reduced.cols());
435
+
436
+ Eigen::MatrixXd result;
437
+ if(m_unconstrained_locs.size())
438
+ {
439
+ result = m_system_lu.solve(r_reduced);
440
+ }
441
+
442
+ // map back to full variable set:
443
+ // return torch.matmul(self.U, x_r) + x_o_filtered
444
+ for(uint32_t i = 0; i < numRows_reduced; ++i)
445
+ {
446
+ uint32_t ifull = m_unconstrained_locs[i];
447
+ for(uint32_t j = 0; j < numCols; ++j)
448
+ {
449
+ x(ifull, j) = result(i, j);
450
+ }
451
+ }
452
+ for(auto i : m_constrained_locs)
453
+ {
454
+ for(uint32_t j = 0; j < numCols; ++j)
455
+ {
456
+ x(i, j) = x_o(i, j);
457
+ }
458
+ }
459
+ }
460
+
461
+ void TrajectoryCorrector::z_update(
462
+ Eigen::MatrixXd &z,
463
+ const Eigen::MatrixXd &x,
464
+ const Eigen::MatrixXd &z_t,
465
+ const Eigen::MatrixXd &u
466
+ ) const
467
+ {
468
+ uint32_t numCols = uint32_t(z.cols());
469
+
470
+ for(uint32_t i = 0; i < m_margin_locs.size(); ++i)
471
+ {
472
+
473
+ // z_diffs = S x + u - z_t
474
+ uint32_t ifull = m_margin_locs[i];
475
+ for(uint32_t j = 0; j < numCols; ++j)
476
+ {
477
+ z(i, j) = x(ifull, j) + u(i, j) - z_t(i, j);
478
+ }
479
+
480
+ // find the norm of the current z diff vector:
481
+ double z_diff_norm = 0.0;
482
+ for(uint32_t j = 0; j < numCols; ++j)
483
+ {
484
+ double z_diff = z(i, j);
485
+ z_diff_norm += z_diff * z_diff;
486
+ }
487
+ z_diff_norm = sqrt(z_diff_norm);
488
+
489
+ // if the norm is greater than the margin size, we need to rescale
490
+ // the diff:
491
+ if( z_diff_norm > m_margin_vals[i] )
492
+ {
493
+ for(uint32_t j = 0; j < numCols; ++j)
494
+ {
495
+ z(i, j) = z(i, j) * m_margin_vals[i] / z_diff_norm;
496
+ }
497
+ }
498
+
499
+ // add the diff back on to the target:
500
+ for(uint32_t j = 0; j < numCols; ++j)
501
+ {
502
+ z(i, j) = z_t(i, j) + z(i, j);
503
+ }
504
+ }
505
+ }
506
+
507
+ void TrajectoryCorrector::u_update(
508
+ Eigen::MatrixXd &u,
509
+ const Eigen::MatrixXd &x,
510
+ const Eigen::MatrixXd &z
511
+ ) const
512
+ {
513
+ uint32_t numCols = uint32_t(z.cols());
514
+
515
+ // u += S x - z
516
+ for(uint32_t i = 0; i < m_margin_locs.size(); ++i)
517
+ {
518
+ uint32_t ifull = m_margin_locs[i];
519
+ for(uint32_t j = 0; j < numCols; ++j)
520
+ {
521
+ u(i,j) += x(ifull, j) - z(i,j);
522
+ }
523
+ }
524
+ }
MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.h ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include <Eigen/Sparse>
9
+
10
+ class TrajectoryCorrector
11
+ {
12
+ public:
13
+
14
+ static void computeDiffMats(
15
+ Eigen::SparseMatrix<double>& V,
16
+ Eigen::SparseMatrix<double>& A,
17
+ uint32_t N,
18
+ const Eigen::VectorXd& velocityWeights = Eigen::VectorXd(),
19
+ Eigen::MatrixXd* v_rhs = nullptr,
20
+ Eigen::MatrixXd* a_rhs = nullptr
21
+ );
22
+
23
+ TrajectoryCorrector(
24
+ const Eigen::VectorXd& margins,
25
+ float pos_weight,
26
+ float vel_weight,
27
+ float acc_weight,
28
+ const Eigen::VectorXd& velocityWeights = Eigen::VectorXd(),
29
+ uint32_t admm_iters=100 );
30
+
31
+ void Interpolate(
32
+ Eigen::MatrixXd& ret,
33
+ const Eigen::MatrixXd& observations,
34
+ const Eigen::MatrixXd& ref_positions
35
+ ) const;
36
+
37
+ void x_update(
38
+ Eigen::MatrixXd& x,
39
+ const Eigen::MatrixXd& z,
40
+ const Eigen::MatrixXd& u,
41
+ const Eigen::MatrixXd& x_t,
42
+ const Eigen::MatrixXd& x_o
43
+ ) const;
44
+
45
+ void z_update(
46
+ Eigen::MatrixXd& z,
47
+ const Eigen::MatrixXd& x,
48
+ const Eigen::MatrixXd& z_t,
49
+ const Eigen::MatrixXd& u
50
+ ) const;
51
+
52
+ void u_update(
53
+ Eigen::MatrixXd& u,
54
+ const Eigen::MatrixXd& x,
55
+ const Eigen::MatrixXd& z
56
+ ) const;
57
+
58
+ float admm_stepsize() const { return m_admm_stepsize; }
59
+
60
+ const std::vector<uint32_t>& margin_locs() { return m_margin_locs; }
61
+
62
+ private:
63
+
64
+ Eigen::SparseMatrix<double> m_N;
65
+ Eigen::SparseLU<Eigen::SparseMatrix<double>> m_system_lu;
66
+
67
+ uint32_t m_admm_iters;
68
+
69
+ std::vector<uint32_t> m_margin_locs;
70
+ std::vector<double> m_margin_vals;
71
+
72
+ std::vector<uint32_t> m_unconstrained_locs;
73
+ std::vector<uint32_t> m_constrained_locs;
74
+
75
+ float m_admm_stepsize;
76
+
77
+ };
MotionCorrection/src/cpp/AnimProcessing/Utility.cpp ADDED
@@ -0,0 +1,1197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "TrajectoryCorrector.h"
7
+ #include "InverseKinematics.h"
8
+
9
+ #include "Utility.h"
10
+
11
+ #include <map>
12
+ #include <array>
13
+ #include <cmath>
14
+ #include <cstdlib>
15
+ #include <iostream>
16
+ using Pose = std::vector<Math::Transform>;
17
+
18
+ static const float pos_weight = 0.001f;
19
+ static const float vel_weight = 1.0f;
20
+ static const float acc_weight = 10.0f;
21
+
22
+
23
+ namespace {
24
+
25
+ // Enable with: MOTIONCORRECTION_DEBUG_INTERVALS=1
26
+ // Default: off (no Interval printing).
27
+ bool DebugPrintIntervalsEnabled()
28
+ {
29
+ const char* v = std::getenv("MOTIONCORRECTION_DEBUG_INTERVALS");
30
+ if (v == nullptr || v[0] == '\0')
31
+ {
32
+ return false;
33
+ }
34
+ // Treat "0" as false; any other non-empty value enables.
35
+ return v[0] != '0';
36
+ }
37
+
38
+
39
+ void FilterContactIntervals(
40
+ std::vector<std::pair<int, int>>& contactIntervals,
41
+ const std::vector<float>& mask,
42
+ bool one_bone_contact = false)
43
+ {
44
+ std::vector<uint32_t> keepIntervals;
45
+ for (size_t i = 0; i < contactIntervals.size(); ++i)
46
+ {
47
+ const auto& interval = contactIntervals[i];
48
+
49
+ bool startConstrained = (interval.first != 0 && mask[interval.first - 1]);
50
+ bool endConstrained;
51
+
52
+ endConstrained = (interval.second != mask.size() && mask[interval.second]);
53
+
54
+ if (one_bone_contact)
55
+ {
56
+ if (startConstrained || endConstrained)
57
+ {
58
+ continue;
59
+ }
60
+ }
61
+ else
62
+ {
63
+ // If both the start and end of the contact interval are masked,
64
+ // there's no way we can correct the contact without popping, so
65
+ // let's filter these out:
66
+ if (startConstrained && endConstrained)
67
+ {
68
+ continue;
69
+ }
70
+ }
71
+
72
+ keepIntervals.push_back(i);
73
+ }
74
+
75
+ for (size_t i = 0; i < keepIntervals.size(); ++i)
76
+ {
77
+ contactIntervals[i] = contactIntervals[keepIntervals[i]];
78
+ }
79
+ contactIntervals.resize(keepIntervals.size());
80
+ }
81
+
82
+ std::vector<std::pair<int, int>> ComputeContactIntervals(
83
+ const std::vector<float>& contacts,
84
+ const std::vector<float>& mask,
85
+ float contactThreshold)
86
+ {
87
+ // turn off the contacts for all frames that are constrained/masked:
88
+ std::vector<float> contactsNoMask = contacts;
89
+ for (size_t i = 0; i < mask.size(); ++i)
90
+ {
91
+ if (mask[i])
92
+ {
93
+ contactsNoMask[i] = 0;
94
+ }
95
+ }
96
+
97
+ // Find intervals that are in contact:
98
+ std::vector<std::pair<int, int>> contactIntervals;
99
+ int start = -1;
100
+ for (int frame = 0; frame < mask.size(); ++frame)
101
+ {
102
+ bool isContact = contactsNoMask[frame] > contactThreshold;
103
+ if (isContact && start == -1)
104
+ {
105
+ start = frame;
106
+ }
107
+ else if (!isContact && start != -1)
108
+ {
109
+ contactIntervals.emplace_back(start, frame);
110
+ start = -1;
111
+ }
112
+ }
113
+
114
+ // Close the final interval if needed:
115
+ if (start != -1)
116
+ {
117
+ contactIntervals.emplace_back(start, mask.size());
118
+ }
119
+ return contactIntervals;
120
+ }
121
+
122
+ void FindContactPoints(
123
+ std::vector<Math::Vector> &points,
124
+ std::vector<int> &inContact,
125
+ const std::vector<int>& joint_parents_vec,
126
+ int32_t jointIndex,
127
+ const std::vector<Pose> &poses,
128
+ const std::vector<std::pair<int, int>>& contactIntervals,
129
+ const std::vector<float>& mask,
130
+ size_t frameCount,
131
+ float minHeight)
132
+ {
133
+ // Find a representative frame for each interval.
134
+ // If the interval starts after a masked frame, use the start
135
+ // of the interval, if it ends before a mask use the end,
136
+ // otherwise use the middle frame.
137
+ inContact.clear();
138
+ inContact.resize(frameCount, 0);
139
+ points.clear();
140
+ points.resize(frameCount);
141
+ for (size_t i = 0; i < contactIntervals.size(); ++i)
142
+ {
143
+ const auto& interval = contactIntervals[i];
144
+ int frame = -1;
145
+ bool startConstrained = (interval.first != 0 && mask[interval.first - 1]);
146
+ bool endConstrained;
147
+
148
+ endConstrained = (interval.second != mask.size() && mask[interval.second]);
149
+
150
+ // Debug output (opt-in via env var)
151
+ if (DebugPrintIntervalsEnabled())
152
+ {
153
+ std::cout << "Interval " << i << ": start=" << interval.first
154
+ << ", end=" << interval.second
155
+ << ", startConstrained=" << startConstrained
156
+ << ", endConstrained=" << endConstrained << std::endl;
157
+ }
158
+
159
+ if(startConstrained)
160
+ {
161
+ // If the interval starts on a constraint, use the constrained frame
162
+ // as a target (doing this modulo mask.size() in case we're looping)
163
+ frame = interval.first - 1;
164
+ }
165
+ else if (endConstrained)
166
+ {
167
+ // If the interval ends on a constraint, use the constrained frame
168
+ // as a target:
169
+ frame = interval.second;
170
+ }
171
+ else
172
+ {
173
+ // Otherwise use the midpoint of the interval:
174
+ frame = (interval.first + interval.second) / 2;
175
+ }
176
+
177
+ // get the target point:
178
+ Math::Vector target = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, poses[frame]).GetTranslation();
179
+ for(int i = interval.first; i < interval.second; ++i)
180
+ {
181
+ Math::Vector framePt = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, poses[i]).GetTranslation();
182
+ inContact[i] = 1;
183
+ points[i] = target;
184
+ if (!startConstrained && !endConstrained)
185
+ {
186
+ points[i].SetY(std::max(framePt.GetY(), minHeight));
187
+ // std::cout << " Frame " << i << ": SetY with framePt.GetY()=" << framePt.GetY()
188
+ // << ", minHeight=" << minHeight << std::endl;
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ float TargetReachFalloff(
195
+ const std::vector<int>& joint_parents_vec,
196
+ const Pose& defaultPose,
197
+ int32_t jointIndex,
198
+ Animation::IKType ikType,
199
+ const Math::Vector& target,
200
+ const Pose& pose,
201
+ const Math::Transform& rootTx = Math::Transform::Identity)
202
+ {
203
+ float maxReach = defaultPose[jointIndex].GetTranslation().GetLength3();
204
+ if (ikType == Animation::IKType::kTwoBone)
205
+ {
206
+ jointIndex = joint_parents_vec[jointIndex];
207
+ ASSERT(jointIndex > -1);
208
+ maxReach += defaultPose[jointIndex].GetTranslation().GetLength3();
209
+ }
210
+ // Get base joint world Tx
211
+ jointIndex = joint_parents_vec[jointIndex];
212
+ ASSERT(jointIndex > -1);
213
+ const auto worldTx = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, pose, rootTx);
214
+
215
+ // Gaussian falloff
216
+ float targetDist = target.GetDistance3(worldTx.GetTranslation());
217
+ float tmp = Math::Max(targetDist / maxReach - 0.99f, 0.f) / 0.01f;
218
+ tmp = tmp * tmp;
219
+ return std::exp(-2.f * tmp * tmp);
220
+ }
221
+
222
+ void CorrectHipsY(
223
+ std::vector<Pose>& poses,
224
+ const std::vector<Pose>& targetPoses,
225
+ const std::vector<float>& fullBodyMask,
226
+ const std::vector<Animation::ContactInfo>& contacts,
227
+ float contactThreshold
228
+ )
229
+ {
230
+ // Correct the y coordinates of the root.
231
+ auto N = poses.size();
232
+ Eigen::MatrixXd x(N, 1);
233
+ Eigen::MatrixXd observations(N, 1);
234
+ Eigen::MatrixXd xfixed(N, 1);
235
+
236
+ // Fill in the initial trajectory (x) and the values we want to hit when we
237
+ // warp it (observations):
238
+ Eigen::VectorXd yCorrectMargins(N);
239
+ for(size_t frame = 0; frame < N; ++frame)
240
+ {
241
+ yCorrectMargins[frame] = fullBodyMask[frame] ? 0.0f : -1.0f;
242
+ x(frame, 0) = ((float*)&poses[frame][0].GetTranslation())[1];
243
+ observations(frame, 0) = ((float*)&targetPoses[frame][0].GetTranslation())[1];
244
+ }
245
+
246
+ TrajectoryCorrector ycorrector(
247
+ yCorrectMargins,
248
+ pos_weight * 10,
249
+ vel_weight,
250
+ acc_weight * 0.1f
251
+ );
252
+ ycorrector.Interpolate(
253
+ xfixed,
254
+ observations,
255
+ x
256
+ );
257
+
258
+ // fill channel again:
259
+ for (uint32_t frame = 0; frame < N; ++frame)
260
+ {
261
+ ((float*)&poses[frame][0].GetTranslation())[1] = float(xfixed(frame, 0));
262
+ }
263
+ }
264
+
265
+ void SmoothChannels(
266
+ Eigen::MatrixXd &x,
267
+ const std::vector<float>& mask
268
+ )
269
+ {
270
+ for( uint32_t i=0; i < mask.size(); ++i)
271
+ {
272
+ uint32_t i_prev = i == 0 ? 0 : i-1;
273
+ uint32_t i_next = std::min(uint32_t(i+1), uint32_t(mask.size()-1));
274
+ if(i > 0 && mask[i] > 0 && mask[i_prev] == 0)
275
+ {
276
+ // if the previous frame is unconstrained and the current frame is constrained,
277
+ // replace the current frame with the average of its neighbors:
278
+ for(long j=0; j < x.cols(); ++j)
279
+ {
280
+ x(i, j) = 0.5f * (x(i_prev, j) + x(i_next, j));
281
+ }
282
+ }
283
+ if(mask[i] > 0 && mask[i_next] == 0)
284
+ {
285
+ // if the next frame is unconstrained and the current frame is constrained,
286
+ // replace the current frame with the average of its neighbors:
287
+ for(long j=0; j < x.cols(); ++j)
288
+ {
289
+ x(i, j) = 0.5f * (x(i_prev, j) + x(i_next, j));
290
+ }
291
+ }
292
+ }
293
+ }
294
+
295
+
296
+ void CorrectHipsXZ(
297
+ std::vector<Pose>& poses,
298
+ const std::vector<Pose>& targetPoses,
299
+ const std::vector<float>& fullBodyMask,
300
+ const std::vector<float>& rootMask,
301
+ const std::vector<Animation::ContactInfo>& endEffectorPins,
302
+ const Eigen::VectorXd& velocity_weights,
303
+ float root_margin
304
+ )
305
+ {
306
+ auto N = poses.size();
307
+ Eigen::VectorXd margins(N);
308
+ for( size_t i = 0; i < N; ++i )
309
+ {
310
+ margins[i] = fullBodyMask[i] ? 0.0f : -1.0f;
311
+ }
312
+
313
+ std::vector<float> rootCombinedMask(N, 0.0f);
314
+ for(size_t i = 0; i < N; ++i)
315
+ {
316
+ rootCombinedMask[i] = (fullBodyMask[i] > 0) || (rootMask[i] > 0);
317
+ if(rootMask[i] > 0 && margins[i] != 0)
318
+ {
319
+ margins[i] = root_margin;
320
+ }
321
+ for (auto& c : endEffectorPins)
322
+ {
323
+ if (c.contactMask[i] && margins[i] != 0)
324
+ {
325
+ margins[i] = root_margin;
326
+ }
327
+ }
328
+ }
329
+ TrajectoryCorrector xzcorrector(
330
+ margins,
331
+ pos_weight,
332
+ vel_weight,
333
+ acc_weight,
334
+ velocity_weights
335
+ );
336
+
337
+ // Enforce pose constraints on root xz trajectory:
338
+ Eigen::MatrixXd x(N, 2);
339
+ Eigen::MatrixXd observations(N, 2);
340
+ Eigen::MatrixXd x_fixed(N, 2);
341
+
342
+ observations.setZero();
343
+ for (uint32_t frame = 0; frame < N; ++frame)
344
+ {
345
+ x(frame, 0) = ((float*)&poses[frame][0].GetTranslation())[0];
346
+ x(frame, 1) = ((float*)&poses[frame][0].GetTranslation())[2];
347
+
348
+ observations(frame, 0) = ((float*)&targetPoses[frame][0].GetTranslation())[0];
349
+ observations(frame, 1) = ((float*)&targetPoses[frame][0].GetTranslation())[2];
350
+ }
351
+
352
+ SmoothChannels(x, rootCombinedMask);
353
+
354
+ xzcorrector.Interpolate(
355
+ x_fixed,
356
+ observations,
357
+ x
358
+ );
359
+
360
+ // fill channels again:
361
+ for (uint32_t frame = 0; frame < N; ++frame)
362
+ {
363
+ ((float*)&poses[frame][0].GetTranslation())[0] = float(x_fixed(frame, 0));
364
+ ((float*)&poses[frame][0].GetTranslation())[2] = float(x_fixed(frame, 1));
365
+ }
366
+ }
367
+
368
+ void CorrectRotationsForBone(
369
+ std::vector<Pose>& poses,
370
+ const std::vector<Pose>& targetPoses,
371
+ const std::vector<float>& mask,
372
+ const TrajectoryCorrector& corrector,
373
+ int boneIdx,
374
+ bool performChannelSmoothing)
375
+ {
376
+ auto N = poses.size();
377
+ Eigen::MatrixXd x(N, 1);
378
+ Eigen::MatrixXd observations(N, 1);
379
+ observations.setZero();
380
+ Eigen::MatrixXd x_fixed(N, 1);
381
+
382
+ // Quaternion components can flip when they pass through 180 degree
383
+ // rotations, so let's convert all the quaternions in this channel to
384
+ // the forward/up vector representation, modify them, then convert back
385
+ // to quaternions:
386
+
387
+ // convert time series to 6d forward/up:
388
+ std::vector<float> forwardUp(6 * N);
389
+ std::vector<float> targetForwardUp(6 * N);
390
+ for (uint32_t frame = 0; frame < N; ++frame)
391
+ {
392
+ auto q = poses[frame][boneIdx].GetRotation();
393
+ auto forward = q.ZAxis();
394
+ auto up = q.YAxis();
395
+ forwardUp[N * 0 + frame] = forward.GetX();
396
+ forwardUp[N * 1 + frame] = forward.GetY();
397
+ forwardUp[N * 2 + frame] = forward.GetZ();
398
+ forwardUp[N * 3 + frame] = up.GetX();
399
+ forwardUp[N * 4 + frame] = up.GetY();
400
+ forwardUp[N * 5 + frame] = up.GetZ();
401
+
402
+ q = targetPoses[frame][boneIdx].GetRotation();
403
+ forward = q.ZAxis();
404
+ up = q.YAxis();
405
+ targetForwardUp[N * 0 + frame] = forward.GetX();
406
+ targetForwardUp[N * 1 + frame] = forward.GetY();
407
+ targetForwardUp[N * 2 + frame] = forward.GetZ();
408
+ targetForwardUp[N * 3 + frame] = up.GetX();
409
+ targetForwardUp[N * 4 + frame] = up.GetY();
410
+ targetForwardUp[N * 5 + frame] = up.GetZ();
411
+ }
412
+
413
+ // correct trajectories:
414
+ for (uint32_t dim = 0; dim < 6; ++dim)
415
+ {
416
+ for (uint32_t frame = 0; frame < N; ++frame)
417
+ {
418
+ x(frame, 0) = forwardUp[N * dim + frame];
419
+ observations(frame, 0) = mask[frame] * targetForwardUp[N * dim + frame];
420
+ }
421
+
422
+ if (performChannelSmoothing)
423
+ {
424
+ SmoothChannels(x, mask);
425
+ }
426
+
427
+ corrector.Interpolate(
428
+ x_fixed,
429
+ observations,
430
+ x
431
+ );
432
+
433
+ // fill channel again:
434
+ for (uint32_t frame = 0; frame < N; ++frame)
435
+ {
436
+ forwardUp[N * dim + frame] = float(x_fixed(frame, 0));
437
+ }
438
+ }
439
+
440
+ for (uint32_t frame = 0; frame < N; ++frame)
441
+ {
442
+ Math::Vector forward = { forwardUp[N * 0 + frame] ,forwardUp[N * 1 + frame] ,forwardUp[N * 2 + frame] };
443
+ Math::Vector up = { forwardUp[N * 3 + frame] ,forwardUp[N * 4 + frame] ,forwardUp[N * 5 + frame] };
444
+
445
+ forward.Normalize3();
446
+ up.Normalize3();
447
+
448
+ poses[frame][boneIdx].SetRotation(Math::Quaternion::LookRotation(forward, up));
449
+ }
450
+ }
451
+
452
+ void CorrectJointRotations(
453
+ std::vector<Pose>& poses,
454
+ const std::vector<Pose>& targetPoses,
455
+ const std::vector<float>& fullBodyMask,
456
+ const Eigen::VectorXd& velocity_weights
457
+ )
458
+ {
459
+ auto N = poses.size();
460
+
461
+ // Create a trajectory corrector for fixing the full body fullBodyMask positions:
462
+ Eigen::VectorXd margins(N);
463
+ for( size_t i = 0; i < N; ++i )
464
+ {
465
+ margins[i] = fullBodyMask[i] ? 0.0f : -1.0f;
466
+ }
467
+ TrajectoryCorrector corrector(
468
+ margins,
469
+ pos_weight * 10,
470
+ vel_weight,
471
+ acc_weight,
472
+ velocity_weights
473
+ );
474
+
475
+ for (uint32_t boneIdx = 0; boneIdx < poses[0].size(); ++boneIdx)
476
+ {
477
+ CorrectRotationsForBone(
478
+ poses,
479
+ targetPoses,
480
+ fullBodyMask,
481
+ corrector,
482
+ boneIdx,
483
+ true
484
+ );
485
+ }
486
+ }
487
+
488
+ void DoEffectorIK(
489
+ std::vector<Pose>& poses,
490
+ const std::vector<Pose>& targetPoses,
491
+ const std::vector<float>& fullBodyMask,
492
+ const std::vector<Animation::ContactInfo>& endEffectorPins,
493
+ const std::vector<int>& joint_parents_vec,
494
+ const std::vector<Math::Transform>& defaultPose
495
+ )
496
+ {
497
+ // Apply IK for effector pins
498
+ auto N = poses.size();
499
+ std::map<uint32_t, std::vector<float>> jointCorrectionMasks;
500
+ std::vector<Pose> ikFixedPoses = poses;
501
+ for (auto& c : endEffectorPins)
502
+ {
503
+ auto jointIdx = c.jointIndex;
504
+
505
+ if(jointCorrectionMasks[jointIdx].empty())
506
+ {
507
+ // initialize to the full body constraint mask because we
508
+ // want to constrain that anyway:
509
+ jointCorrectionMasks[jointIdx] = fullBodyMask;
510
+ }
511
+
512
+ // Add a trajectory correction mask for the parent joint:
513
+ auto parentIdx = joint_parents_vec[jointIdx];
514
+ if(jointCorrectionMasks[parentIdx].empty())
515
+ {
516
+ // initialize to the full body constraint mask because we
517
+ // want to constrain that anyway:
518
+ jointCorrectionMasks[parentIdx] = fullBodyMask;
519
+ }
520
+
521
+ // Add a trajectory correction mask for its parent if this is
522
+ // 2 bone IK:
523
+ auto parentParentIdx = joint_parents_vec[parentIdx];
524
+ if(c.contactType == Animation::kTwoBone)
525
+ {
526
+ if(jointCorrectionMasks[parentParentIdx].empty())
527
+ {
528
+ // initialize to the full body constraint mask because we
529
+ // want to constrain that anyway:
530
+ jointCorrectionMasks[parentParentIdx] = fullBodyMask;
531
+ }
532
+ }
533
+
534
+ for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame)
535
+ {
536
+ if (c.contactMask[fixFrame])
537
+ {
538
+ const auto targetGlobalTransform = Animation::JointLocalToGlobal(joint_parents_vec, jointIdx, targetPoses[fixFrame]);
539
+
540
+ // flag the parent joint as fixed in its correction mask:
541
+ jointCorrectionMasks[parentIdx][fixFrame] = 1;
542
+ switch(c.contactType)
543
+ {
544
+ case Animation::kOneBone:
545
+ {
546
+ IK::OneBoneIk(
547
+ ikFixedPoses[fixFrame],
548
+ Math::Transform::Identity,
549
+ jointIdx,
550
+ 1.0,
551
+ targetGlobalTransform.GetTranslation(),
552
+ joint_parents_vec
553
+ );
554
+ break;
555
+ }
556
+ case Animation::kTwoBone:
557
+ {
558
+ // flag the parent parent joint as fixed in its correction mask:
559
+ jointCorrectionMasks[parentParentIdx][fixFrame] = 1;
560
+ IK::TwoBoneIk(
561
+ ikFixedPoses[fixFrame],
562
+ Math::Transform::Identity,
563
+ jointIdx,
564
+ 1.0,
565
+ targetGlobalTransform.GetTranslation(),
566
+ joint_parents_vec,
567
+ c.hintOffset
568
+ );
569
+ break;
570
+ }
571
+ }
572
+
573
+ // now we need to fix things so the global rotation of the joint
574
+ // matches the input:
575
+ jointCorrectionMasks[jointIdx][fixFrame] = 1;
576
+ auto parentGlobalTransform = Animation::JointLocalToGlobal(joint_parents_vec, parentIdx, ikFixedPoses[fixFrame]);
577
+ ikFixedPoses[fixFrame][jointIdx].SetRotation(
578
+ targetGlobalTransform.GetRotation() * parentGlobalTransform.GetRotation().GetConjugate()
579
+ );
580
+
581
+ }
582
+ }
583
+ }
584
+
585
+ // Applying the effector pin IK introduces popping into the animation,
586
+ // so let's apply the interpolator to all the joints we modified so as to
587
+ // line the trajectory up properly again:
588
+ Eigen::VectorXd margins(N);
589
+ for( auto &kv : jointCorrectionMasks)
590
+ {
591
+ for( size_t i = 0; i < N; ++i )
592
+ {
593
+ margins[i] = kv.second[i] ? 0.0f : -1.0f;
594
+ }
595
+ TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight);
596
+
597
+ CorrectRotationsForBone(
598
+ poses,
599
+ ikFixedPoses,
600
+ kv.second,
601
+ corrector,
602
+ kv.first,
603
+ false
604
+ );
605
+ }
606
+ }
607
+
608
+ void DoContactIK(
609
+ std::vector<Pose>& poses,
610
+ const std::vector<float>& fullBodyMask,
611
+ const std::vector<Animation::ContactInfo>& contacts,
612
+ const std::vector<Animation::ContactInfo>& endEffectorPins,
613
+ const std::vector<int>& joint_parents_vec,
614
+ const std::vector<Math::Transform>& defaultPose,
615
+ float contactThreshold,
616
+ bool has_double_ankle_joints
617
+ )
618
+ {
619
+ auto N = poses.size();
620
+ Eigen::VectorXd margins = Eigen::VectorXd::Zero(N);
621
+
622
+ // Apply IK to stabilize limbs on contacts
623
+ std::map<uint32_t, std::vector<float>> jointCorrectionMasks;
624
+ std::vector<Pose> ikFixedPoses = poses;
625
+
626
+ // Save original poses before any modifications (for double ankle correction later)
627
+ const std::vector<Pose> originalPoses = poses;
628
+
629
+ // Track which frames were corrected for each 2-bone contact (for double ankle correction later)
630
+ std::map<uint32_t, std::vector<bool>> twoBoneContactFrames;
631
+
632
+ auto addEndEffectorMask = [&](uint32_t jointIdx, uint32_t parentIdx, std::vector<float>& jointMask)
633
+ {
634
+ auto it = std::find_if(
635
+ endEffectorPins.begin(), endEffectorPins.end(),
636
+ [&](const auto &c)
637
+ {
638
+ if(jointIdx == c.jointIndex)
639
+ {
640
+ return true;
641
+ }
642
+ return false;
643
+ }
644
+ );
645
+ if(it == endEffectorPins.end())
646
+ {
647
+ // We could be correcting the toe joint, in which case we need to use
648
+ // the parent joint instead:
649
+ it = std::find_if(
650
+ endEffectorPins.begin(), endEffectorPins.end(),
651
+ [&](const auto &c)
652
+ {
653
+ if(parentIdx == c.jointIndex)
654
+ {
655
+ return true;
656
+ }
657
+ return false;
658
+ }
659
+ );
660
+ }
661
+ if(it != endEffectorPins.end())
662
+ {
663
+ const auto &msk = it->contactMask;
664
+ for(size_t i=0; i < msk.size(); ++i)
665
+ {
666
+ if(msk[i])
667
+ {
668
+ jointMask[i] = 1.0f;
669
+ }
670
+ }
671
+ }
672
+ };
673
+
674
+ // Process two bone contacts first:
675
+ for (auto& c : contacts)
676
+ {
677
+ if(c.contactType != Animation::kTwoBone)
678
+ {
679
+ continue;
680
+ }
681
+ const auto jointIdx = c.jointIndex;
682
+ auto parentIdx = joint_parents_vec[jointIdx];
683
+ auto parentParentIdx = joint_parents_vec[parentIdx];
684
+
685
+ auto jointMask = fullBodyMask;
686
+ addEndEffectorMask(jointIdx, parentIdx, jointMask);
687
+
688
+ // We'll actually be modifying 3 joints here:
689
+ // * The two joints immediately up in the hierarchy because of the 2 bone IK
690
+ // * The joint itself because we restore its original global rotation
691
+ if(jointCorrectionMasks[parentIdx].empty())
692
+ {
693
+ jointCorrectionMasks[parentIdx] = jointMask;
694
+ }
695
+ if(jointCorrectionMasks[parentParentIdx].empty())
696
+ {
697
+ jointCorrectionMasks[parentParentIdx] = jointMask;
698
+ }
699
+ if(jointCorrectionMasks[jointIdx].empty())
700
+ {
701
+ jointCorrectionMasks[jointIdx] = jointMask;
702
+ }
703
+
704
+ // Compute the intervals in which the joint is in contact with the floor:
705
+ auto contactIntervals = ComputeContactIntervals(c.contactMask, jointMask, contactThreshold);
706
+ FilterContactIntervals(contactIntervals, jointMask);
707
+
708
+ std::vector<Math::Vector> contactPoints;
709
+ std::vector<int> inContact;
710
+ FindContactPoints(
711
+ contactPoints,
712
+ inContact,
713
+ joint_parents_vec,
714
+ jointIdx,
715
+ poses,
716
+ contactIntervals,
717
+ jointMask,
718
+ c.contactMask.size(),
719
+ c.minHeight
720
+ );
721
+
722
+ for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame)
723
+ {
724
+ if (inContact[fixFrame])
725
+ {
726
+ auto target = contactPoints[fixFrame];
727
+ jointCorrectionMasks[parentIdx][fixFrame] = 1.0f;
728
+ jointCorrectionMasks[parentParentIdx][fixFrame] = 1.0f;
729
+ jointCorrectionMasks[jointIdx][fixFrame] = 1.0f;
730
+
731
+ // Track this frame for double ankle correction later
732
+ if (has_double_ankle_joints)
733
+ {
734
+ if (twoBoneContactFrames[jointIdx].empty())
735
+ twoBoneContactFrames[jointIdx].resize(fullBodyMask.size(), false);
736
+ twoBoneContactFrames[jointIdx][fixFrame] = true;
737
+ }
738
+
739
+ // save the original global rotation of the joint:
740
+ auto jointGlobalRotation = Animation::JointLocalToGlobal(
741
+ joint_parents_vec,
742
+ jointIdx,
743
+ ikFixedPoses[fixFrame]
744
+ ).GetRotation();
745
+
746
+ const float w = TargetReachFalloff(
747
+ joint_parents_vec,
748
+ defaultPose,
749
+ jointIdx,
750
+ c.contactType,
751
+ target,
752
+ ikFixedPoses[fixFrame]
753
+ );
754
+ // std::cout << "Frame " << fixFrame << ": w=" << w << std::endl;
755
+
756
+ // apply the 2 bone IK:
757
+ auto origParentRotation = ikFixedPoses[fixFrame][parentIdx].GetRotation();
758
+ auto origParentParentRotation = ikFixedPoses[fixFrame][parentParentIdx].GetRotation();
759
+ IK::TwoBoneIk(
760
+ ikFixedPoses[fixFrame],
761
+ Math::Transform::Identity,
762
+ jointIdx,
763
+ 1.0f,
764
+ target,
765
+ joint_parents_vec,
766
+ c.hintOffset
767
+ );
768
+ ikFixedPoses[fixFrame][parentIdx].SetRotation(Math::Quaternion::SLerp(origParentRotation, ikFixedPoses[fixFrame][parentIdx].GetRotation(), w));
769
+ ikFixedPoses[fixFrame][parentParentIdx].SetRotation(Math::Quaternion::SLerp(origParentParentRotation, ikFixedPoses[fixFrame][parentParentIdx].GetRotation(), w));
770
+
771
+ // restore previous global rotation of this joint:
772
+ auto parentGloblalRotation = Animation::JointLocalToGlobal(
773
+ joint_parents_vec,
774
+ parentIdx,
775
+ ikFixedPoses[fixFrame]
776
+ ).GetRotation();
777
+
778
+ jointCorrectionMasks[jointIdx][fixFrame] = 1.0f;
779
+ ikFixedPoses[fixFrame][jointIdx].SetRotation(
780
+ jointGlobalRotation * parentGloblalRotation.GetConjugate()
781
+ );
782
+
783
+ auto result = Animation::JointLocalToGlobal(
784
+ joint_parents_vec,
785
+ jointIdx,
786
+ ikFixedPoses[fixFrame]
787
+ ).GetTranslation();
788
+ }
789
+ }
790
+
791
+ }
792
+
793
+ for( auto &kv : jointCorrectionMasks)
794
+ {
795
+ for( size_t i = 0; i < N; ++i )
796
+ {
797
+ margins[i] = kv.second[i] ? 0.0f : -1.0f;
798
+ }
799
+ TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight);
800
+ CorrectRotationsForBone(
801
+ poses,
802
+ ikFixedPoses,
803
+ kv.second,
804
+ corrector,
805
+ kv.first,
806
+ false
807
+ );
808
+ }
809
+ jointCorrectionMasks.clear();
810
+
811
+ // Then process one bone contacts:
812
+ for(auto &c : contacts)
813
+ {
814
+ if(c.contactType != Animation::kOneBone)
815
+ {
816
+ continue;
817
+ }
818
+ const auto jointIdx = c.jointIndex;
819
+ auto parentIdx = joint_parents_vec[jointIdx];
820
+
821
+ // We can't touch frames that have been constrained with full body constraints
822
+ // or the end effector constraints for this joint, so let's combine fullBodyMask
823
+ // with the end effector mask for this joint if it exists so we can use that
824
+ // information later:
825
+ auto jointMask = fullBodyMask;
826
+ addEndEffectorMask(jointIdx, parentIdx, jointMask);
827
+
828
+ // Add a trajectory correction mask for the parent joint:
829
+ if(jointCorrectionMasks[parentIdx].empty())
830
+ {
831
+ jointCorrectionMasks[parentIdx] = jointMask;
832
+ }
833
+
834
+ // Compute the intervals in which the joint is in contact with the floor:
835
+ auto contactIntervals = ComputeContactIntervals(c.contactMask, jointMask, contactThreshold);
836
+ FilterContactIntervals(contactIntervals, jointMask, true);
837
+ for(const auto &interval : contactIntervals)
838
+ {
839
+ for (int fixFrame = interval.first; fixFrame < interval.second; ++fixFrame)
840
+ {
841
+ // All we're going to do here is stick the joint to the floor -
842
+ // we're going to allow it to slide from side to side.
843
+
844
+ // Find a target position that lies on the floor by iteratively
845
+ // projecting the joint to the floor (pure laziness really, this could
846
+ // be done analytically):
847
+ Math::Vector parentPos = Animation::JointLocalToGlobal(joint_parents_vec, parentIdx, ikFixedPoses[fixFrame]).GetTranslation();
848
+ Math::Vector target = Animation::JointLocalToGlobal(joint_parents_vec, jointIdx, ikFixedPoses[fixFrame]).GetTranslation();
849
+ float jointLength = (target - parentPos).GetLength3();
850
+ for(int32_t i = 0; i < 10; ++i)
851
+ {
852
+ target.SetY(c.minHeight);
853
+ auto dir = (target - parentPos).GetNormalized3();
854
+ target = parentPos + dir * jointLength;
855
+ }
856
+
857
+ IK::OneBoneIk(
858
+ ikFixedPoses[fixFrame],
859
+ Math::Transform::Identity,
860
+ jointIdx,
861
+ 1.0f,
862
+ target,
863
+ joint_parents_vec
864
+ );
865
+ jointCorrectionMasks[parentIdx][fixFrame] = 1.0f;
866
+ }
867
+ }
868
+
869
+ }
870
+
871
+ // Fixing the contacts with IK will introduce popping into the animation,
872
+ // so let's apply the interpolator to all the joints we modified so as to
873
+ // line the trajectory up properly again:
874
+ for( auto &kv : jointCorrectionMasks)
875
+ {
876
+ for( size_t i = 0; i < N; ++i )
877
+ {
878
+ margins[i] = kv.second[i] ? 0.0f : -1.0f;
879
+ }
880
+ TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight);
881
+ CorrectRotationsForBone(
882
+ poses,
883
+ ikFixedPoses,
884
+ kv.second,
885
+ corrector,
886
+ kv.first,
887
+ false
888
+ );
889
+ }
890
+
891
+ if (has_double_ankle_joints)
892
+ {
893
+ // Maps to save target positions BEFORE 2-bone IK modifies them
894
+ std::map<uint32_t, std::map<uint32_t, Math::Vector>> savedFirstAnkleTargets; // [firstAnkleIdx][frame] -> position
895
+ std::map<uint32_t, std::map<uint32_t, Math::Vector>> savedToeTargets; // [firstAnkleIdx][frame] -> position
896
+ std::map<uint32_t, uint32_t> contactToToeIdx; // firstAnkleIdx -> toeIdx
897
+
898
+ // Find toe joints for each leg
899
+ for (const auto& tc : contacts)
900
+ {
901
+ if (tc.contactType == Animation::kOneBone)
902
+ {
903
+ // The parent of the toe is the 1st ankle
904
+ int parentIdx = joint_parents_vec[tc.jointIndex];
905
+ if (parentIdx >= 0)
906
+ {
907
+ contactToToeIdx[parentIdx] = tc.jointIndex;
908
+ }
909
+ }
910
+ }
911
+
912
+ // For each 2-bone contact, correct the parent (2nd ankle) joint
913
+ for (auto& c : contacts)
914
+ {
915
+ if (c.contactType != Animation::kTwoBone)
916
+ continue;
917
+
918
+ const auto firstAnkleIdx = c.jointIndex;
919
+ const auto secondAnkleIdx = joint_parents_vec[firstAnkleIdx];
920
+ const auto kneeIdx = joint_parents_vec[secondAnkleIdx];
921
+ const auto hipIdx = joint_parents_vec[kneeIdx];
922
+
923
+ if (hipIdx < 0) continue; // safety check
924
+
925
+ // Get saved contact frames for this ankle
926
+ auto it = twoBoneContactFrames.find(firstAnkleIdx);
927
+ if (it == twoBoneContactFrames.end())
928
+ continue;
929
+ const auto& contactFrames = it->second;
930
+
931
+ // Add correction mask for knee and hip
932
+ auto jointMask = fullBodyMask;
933
+ addEndEffectorMask(firstAnkleIdx, secondAnkleIdx, jointMask);
934
+
935
+ if (jointCorrectionMasks[kneeIdx].empty())
936
+ jointCorrectionMasks[kneeIdx] = jointMask;
937
+ if (jointCorrectionMasks[hipIdx].empty())
938
+ jointCorrectionMasks[hipIdx] = jointMask;
939
+
940
+ for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame)
941
+ {
942
+ // Only correct frames where the 1st ankle was corrected
943
+ if (!contactFrames[fixFrame])
944
+ continue;
945
+
946
+ // *** SAVE TARGET POSITIONS BEFORE 2-BONE IK ***
947
+ savedFirstAnkleTargets[firstAnkleIdx][fixFrame] = Animation::JointLocalToGlobal(
948
+ joint_parents_vec, firstAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation();
949
+
950
+ if (contactToToeIdx.count(firstAnkleIdx))
951
+ {
952
+ savedToeTargets[firstAnkleIdx][fixFrame] = Animation::JointLocalToGlobal(
953
+ joint_parents_vec, contactToToeIdx[firstAnkleIdx], ikFixedPoses[fixFrame]).GetTranslation();
954
+ }
955
+
956
+ // Get original global transforms (before any IK corrections)
957
+ auto originalFirstAnkleGlobal = Animation::JointLocalToGlobal(
958
+ joint_parents_vec, firstAnkleIdx, originalPoses[fixFrame]);
959
+ auto originalSecondAnkleGlobal = Animation::JointLocalToGlobal(
960
+ joint_parents_vec, secondAnkleIdx, originalPoses[fixFrame]);
961
+
962
+ // Compute delta from 1st ankle to 2nd ankle in original animation
963
+ auto deltaFirstToSecond = originalFirstAnkleGlobal.GetDeltaToOther(originalSecondAnkleGlobal);
964
+
965
+ // Get corrected 1st ankle global transform
966
+ auto correctedFirstAnkleGlobal = Animation::JointLocalToGlobal(
967
+ joint_parents_vec, firstAnkleIdx, ikFixedPoses[fixFrame]);
968
+
969
+ // Apply the original delta to the corrected 1st ankle to get target for 2nd ankle
970
+ auto target = (deltaFirstToSecond * correctedFirstAnkleGlobal).GetTranslation();
971
+
972
+ // print current and target second ankle positions
973
+ auto currPos = Animation::JointLocalToGlobal(
974
+ joint_parents_vec, secondAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation();
975
+
976
+ // Apply 2-bone IK: Hip -> Knee -> 2nd Ankle
977
+ IK::TwoBoneIk(
978
+ ikFixedPoses[fixFrame],
979
+ Math::Transform::Identity,
980
+ secondAnkleIdx,
981
+ 1.0f,
982
+ target,
983
+ joint_parents_vec,
984
+ c.hintOffset
985
+ );
986
+
987
+ // auto correctedPos = Animation::JointLocalToGlobal(
988
+ // joint_parents_vec, secondAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation();
989
+ // std::cout << "Frame " << fixFrame << ": target second ankle=(" << target.GetX() << ", " << target.GetY() << ", " << target.GetZ() << "), corrected second ankle position=(" << correctedPos.GetX() << ", " << correctedPos.GetY() << ", " << correctedPos.GetZ() << ")" << std::endl;
990
+
991
+ jointCorrectionMasks[kneeIdx][fixFrame] = 1.0f;
992
+ jointCorrectionMasks[hipIdx][fixFrame] = 1.0f;
993
+ }
994
+ }
995
+
996
+ // Smooth the corrected joints
997
+ for (auto& kv : jointCorrectionMasks)
998
+ {
999
+ for (size_t i = 0; i < N; ++i)
1000
+ margins[i] = kv.second[i] ? 0.0f : -1.0f;
1001
+
1002
+ TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight);
1003
+ CorrectRotationsForBone(poses, ikFixedPoses, kv.second, corrector, kv.first, false);
1004
+ }
1005
+
1006
+ // *** PHASE 2: 1-bone IKs to restore 1st ankle and toe ***
1007
+ jointCorrectionMasks.clear();
1008
+
1009
+ for (auto& c : contacts)
1010
+ {
1011
+ if (c.contactType != Animation::kTwoBone)
1012
+ continue;
1013
+
1014
+ const auto firstAnkleIdx = c.jointIndex;
1015
+ const auto secondAnkleIdx = joint_parents_vec[firstAnkleIdx];
1016
+
1017
+ auto it = twoBoneContactFrames.find(firstAnkleIdx);
1018
+ if (it == twoBoneContactFrames.end())
1019
+ continue;
1020
+
1021
+ // Setup correction masks
1022
+ auto jointMask = fullBodyMask;
1023
+ addEndEffectorMask(firstAnkleIdx, secondAnkleIdx, jointMask);
1024
+
1025
+ if (jointCorrectionMasks[secondAnkleIdx].empty())
1026
+ jointCorrectionMasks[secondAnkleIdx] = jointMask;
1027
+ if (jointCorrectionMasks[firstAnkleIdx].empty())
1028
+ jointCorrectionMasks[firstAnkleIdx] = jointMask;
1029
+
1030
+ for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame)
1031
+ {
1032
+ if (!it->second[fixFrame])
1033
+ continue;
1034
+
1035
+ // 1-bone IK: Rotate 2nd ankle so 1st ankle reaches saved target
1036
+ IK::OneBoneIk(
1037
+ ikFixedPoses[fixFrame],
1038
+ Math::Transform::Identity,
1039
+ firstAnkleIdx,
1040
+ 1.0f,
1041
+ savedFirstAnkleTargets[firstAnkleIdx][fixFrame],
1042
+ joint_parents_vec
1043
+ );
1044
+ jointCorrectionMasks[secondAnkleIdx][fixFrame] = 1.0f;
1045
+
1046
+ // auto target = savedFirstAnkleTargets[firstAnkleIdx][fixFrame];
1047
+ // auto corrected = Animation::JointLocalToGlobal(
1048
+ // joint_parents_vec, firstAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation();
1049
+ // std::cout << "Frame " << fixFrame << ": target first ankle=(" << target.GetX() << ", " << target.GetY() << ", " << target.GetZ() << "), corrected first ankle=(" << corrected.GetX() << ", " << corrected.GetY() << ", " << corrected.GetZ() << ")" << std::endl;
1050
+
1051
+ // 1-bone IK: Rotate 1st ankle so toe reaches saved target
1052
+ if (contactToToeIdx.count(firstAnkleIdx) && savedToeTargets[firstAnkleIdx].count(fixFrame))
1053
+ {
1054
+ IK::OneBoneIk(
1055
+ ikFixedPoses[fixFrame],
1056
+ Math::Transform::Identity,
1057
+ contactToToeIdx[firstAnkleIdx],
1058
+ 1.0f,
1059
+ savedToeTargets[firstAnkleIdx][fixFrame],
1060
+ joint_parents_vec
1061
+ );
1062
+ jointCorrectionMasks[firstAnkleIdx][fixFrame] = 1.0f;
1063
+ }
1064
+
1065
+ // target = savedToeTargets[firstAnkleIdx][fixFrame];
1066
+ // corrected = Animation::JointLocalToGlobal(
1067
+ // joint_parents_vec, contactToToeIdx[firstAnkleIdx], ikFixedPoses[fixFrame]).GetTranslation();
1068
+ // std::cout << "Frame " << fixFrame << ": target toe=(" << target.GetX() << ", " << target.GetY() << ", " << target.GetZ() << "), corrected toe=(" << corrected.GetX() << ", " << corrected.GetY() << ", " << corrected.GetZ() << ")" << std::endl;
1069
+ }
1070
+ }
1071
+
1072
+ // Smooth 2nd ankle and 1st ankle
1073
+ for (auto& kv : jointCorrectionMasks)
1074
+ {
1075
+ for (size_t i = 0; i < N; ++i)
1076
+ margins[i] = kv.second[i] ? 0.0f : -1.0f;
1077
+
1078
+ TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight);
1079
+ CorrectRotationsForBone(poses, ikFixedPoses, kv.second, corrector, kv.first, false);
1080
+ }
1081
+ }
1082
+ }
1083
+
1084
+ }
1085
+
1086
+
1087
+ Math::Transform Animation::JointLocalToGlobal(
1088
+ const std::vector<int>& joint_parents_vec,
1089
+ int32_t index,
1090
+ const Pose& localPose,
1091
+ const Math::Transform& rootTx)
1092
+ {
1093
+ Math::Transform worldTx = Math::Transform::Identity;
1094
+ while (index > -1)
1095
+ {
1096
+ worldTx = worldTx * localPose[index];
1097
+ index = joint_parents_vec[index];
1098
+ }
1099
+
1100
+ return worldTx * rootTx;
1101
+ }
1102
+
1103
+ void Animation::CorrectMotion(
1104
+ std::vector<Pose>& poses,
1105
+ const std::vector<Pose>& targetPoses,
1106
+ const std::vector<float>& fullBodyMask,
1107
+ const std::vector<float>& rootMask,
1108
+ const std::vector<ContactInfo>& contacts,
1109
+ const std::vector<ContactInfo>& endEffectorPins,
1110
+ const std::vector<int>& joint_parents_vec,
1111
+ const std::vector<Math::Transform>& defaultPose,
1112
+ float contactThreshold,
1113
+ float root_margin,
1114
+ bool has_double_ankle_joints
1115
+ )
1116
+ {
1117
+
1118
+ // Calculate some weights so we can preserve velocities more strongly on frames where
1119
+ // the root velocity is low
1120
+ const uint32_t N = poses.size();
1121
+ Eigen::VectorXd velocity_weights(N);
1122
+ for (uint32_t frame = 1; frame < N; ++frame)
1123
+ {
1124
+ // work out xz velocity for this frame:
1125
+ float xdiff = poses[frame][0].GetTranslation()[0] - poses[frame - 1][0].GetTranslation()[0];
1126
+ float zdiff = poses[frame][0].GetTranslation()[2] - poses[frame - 1][0].GetTranslation()[2];
1127
+
1128
+ // find velocity magnitude, divided by a typical walking speed:
1129
+ float v_mag = sqrtf(xdiff*xdiff + zdiff*zdiff) / 0.05f;
1130
+
1131
+ // weight lower velocities higher so that the corrector doesn't make the character drift around
1132
+ // when it's supposed to stand still:
1133
+ v_mag = std::max(v_mag, 1.0f/1000.0f);
1134
+ velocity_weights(frame) = 1.0f / v_mag;
1135
+ }
1136
+ velocity_weights[0] = velocity_weights[1];
1137
+
1138
+ // Correct root y coordinates.
1139
+ // This will warp the root y coordinates in "poses" so they match the root y coordinates
1140
+ // in "targetPoses", on frames where the root y coordinates are constrained, ie the frames
1141
+ // where fullBodyMask = 1.
1142
+ // In addition to this, it preserves the root y coordinates in "pose" on frames where foot
1143
+ // contacts are active, to avoid mushiness when characters are jumping.
1144
+ CorrectHipsY(
1145
+ poses,
1146
+ targetPoses,
1147
+ fullBodyMask,
1148
+ contacts,
1149
+ contactThreshold
1150
+ );
1151
+
1152
+ // Correct root xz coordinates:
1153
+ // This will warp the root xz coordinates in "poses" so they match the xz coordinates
1154
+ // in "targetPoses" on frames where fullBodyMask = 1, and warp them so they're within
1155
+ // "root_margin" units of targetPoses on frames where rootMask = 1.
1156
+ CorrectHipsXZ(
1157
+ poses,
1158
+ targetPoses,
1159
+ fullBodyMask,
1160
+ rootMask,
1161
+ endEffectorPins,
1162
+ velocity_weights,
1163
+ root_margin
1164
+ );
1165
+
1166
+ // Correct joint rotations by warping the rotations so they match targetPoses on frames
1167
+ // where fullBodyMask = 1:
1168
+ CorrectJointRotations(
1169
+ poses,
1170
+ targetPoses,
1171
+ fullBodyMask,
1172
+ velocity_weights
1173
+ );
1174
+
1175
+ // Apply IK for end effector pins
1176
+ DoEffectorIK(
1177
+ poses,
1178
+ targetPoses,
1179
+ fullBodyMask,
1180
+ endEffectorPins,
1181
+ joint_parents_vec,
1182
+ defaultPose
1183
+ );
1184
+
1185
+ // Apply IK to stabilize limbs on contacts
1186
+ DoContactIK(
1187
+ poses,
1188
+ fullBodyMask,
1189
+ contacts,
1190
+ endEffectorPins,
1191
+ joint_parents_vec,
1192
+ defaultPose,
1193
+ contactThreshold,
1194
+ has_double_ankle_joints
1195
+ );
1196
+ // std::cout << "Running post processing." << std::endl;
1197
+ }
MotionCorrection/src/cpp/AnimProcessing/Utility.h ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Math/Transform.h"
9
+
10
+ #include <string>
11
+ #include <vector>
12
+
13
+ namespace Animation
14
+ {
15
+ enum IKType {
16
+ kOneBone,
17
+ kTwoBone
18
+ };
19
+
20
+ Math::Transform JointLocalToGlobal(
21
+ const std::vector<int>& joint_parents_vec,
22
+ int32_t index,
23
+ const std::vector<Math::Transform>& localPose,
24
+ const Math::Transform& rootTx = Math::Transform::Identity
25
+ );
26
+
27
+ struct ContactInfo {
28
+ // index IK contact joint:
29
+ int jointIndex;
30
+ // mask indicating which frames are in contact:
31
+ std::vector<float> contactMask;
32
+ // contact type:
33
+ IKType contactType = kTwoBone;
34
+
35
+ // Extra info for TwoBoneIK
36
+ Math::Vector hintOffset = Math::Vector::Zero;
37
+
38
+ float minHeight = 0.0f;
39
+ };
40
+
41
+ void CorrectMotion(
42
+ std::vector< std::vector<Math::Transform> >& poses,
43
+ const std::vector< std::vector<Math::Transform> >& targetPoses,
44
+ const std::vector<float>& mask,
45
+ const std::vector<float>& rootMask,
46
+ const std::vector<ContactInfo>& contacts,
47
+ const std::vector<ContactInfo>& endEffectorPins,
48
+ const std::vector<int>& joint_parents_vec,
49
+ const std::vector<Math::Transform>& defaultPose,
50
+ float contactThreshold,
51
+ float root_margin,
52
+ bool has_double_ankle_joints
53
+ );
54
+ }
MotionCorrection/src/cpp/BindingsPython.cpp ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "AnimProcessing/Utility.h"
7
+
8
+ #ifdef _WIN32
9
+ #pragma warning(push)
10
+ #pragma warning(disable : 4623 4191 4686 4868 5219 4191 4355)
11
+ #endif
12
+ #include <pybind11/pybind11.h>
13
+ #include <pybind11/numpy.h>
14
+ #include <pybind11/stl.h>
15
+ #ifdef _WIN32
16
+ #pragma warning(pop)
17
+ #endif
18
+
19
+ namespace py = pybind11;
20
+
21
+ float strip_nan_inf(float x) noexcept
22
+ {
23
+ if (std::isnan(x)) return 0;
24
+ if (std::isinf(x)) return 0;
25
+ return x;
26
+ }
27
+
28
+ void correct_motion(
29
+ py::array_t<float> &rootTranslations,
30
+ py::array_t<float> &jointRotations,
31
+ const py::array_t<float>& rootTranslationsTarget,
32
+ const py::array_t<float>& jointRotationsTarget,
33
+ const py::array_t<float>& fullPoseMask,
34
+ const py::array_t<float>& leftHandMask,
35
+ const py::array_t<float>& rightHandMask,
36
+ const py::array_t<float>& leftFootMask,
37
+ const py::array_t<float>& rightFootMask,
38
+ const py::array_t<float>& rootMask,
39
+ const py::array_t<float>& contacts,
40
+ const py::list& joint_parents,
41
+ const py::list& joint_ref_translations,
42
+ const py::list& joint_ref_rotations,
43
+ int left_hand_idx,
44
+ int right_hand_idx,
45
+ int left_foot_idx,
46
+ int right_foot_idx,
47
+ float contact_threshold,
48
+ float root_margin,
49
+ bool has_double_ankle_joints
50
+ )
51
+ {
52
+ if(joint_parents.size() != joint_ref_translations.size())
53
+ {
54
+ throw std::runtime_error("correct_motion python bindings: joint_parents and joint_ref_translations must have the same size");
55
+ }
56
+ if(joint_parents.size() != joint_ref_rotations.size())
57
+ {
58
+ throw std::runtime_error("correct_motion python bindings: joint_parents and joint_ref_rotations must have the same size");
59
+ }
60
+ if(left_hand_idx < 0 || right_hand_idx < 0 || left_foot_idx < 0 || right_foot_idx < 0)
61
+ {
62
+ throw std::runtime_error("correct_motion python bindings: left_hand_idx, right_hand_idx, left_foot_idx, and right_foot_idx must be non-negative");
63
+ }
64
+ if(left_hand_idx >= joint_parents.size() || right_hand_idx >= joint_parents.size() || left_foot_idx >= joint_parents.size() || right_foot_idx >= joint_parents.size())
65
+ {
66
+ throw std::runtime_error("correct_motion python bindings: left_hand_idx, right_hand_idx, left_foot_idx, and right_foot_idx must be less than the number of joints");
67
+ }
68
+
69
+ std::vector<Math::Transform> defaultPose(joint_parents.size());
70
+ for (size_t i = 0; i < joint_ref_translations.size(); ++i)
71
+ {
72
+ if (!py::isinstance<py::list>(joint_ref_translations[i]))
73
+ {
74
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_translations to be a list of lists");
75
+ }
76
+ py::list inner_list = joint_ref_translations[i].cast<py::list>();
77
+ if (inner_list.size() != 3) {
78
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_translations to be a list of lists, length 3");
79
+ }
80
+
81
+ if (
82
+ !py::isinstance<py::float_>(inner_list[0]) ||
83
+ !py::isinstance<py::float_>(inner_list[1]) ||
84
+ !py::isinstance<py::float_>(inner_list[2])
85
+ )
86
+ {
87
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_translations to be a list of lists, length 3, float values");
88
+ }
89
+
90
+
91
+ if (!py::isinstance<py::list>(joint_ref_rotations[i]))
92
+ {
93
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_rotations to be a list of lists");
94
+ }
95
+ py::list inner_list_rot = joint_ref_rotations[i].cast<py::list>();
96
+ if (inner_list_rot.size() != 4) {
97
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_rotations to be a list of lists, length 4");
98
+ }
99
+
100
+ if (
101
+ !py::isinstance<py::float_>(inner_list_rot[0]) ||
102
+ !py::isinstance<py::float_>(inner_list_rot[1]) ||
103
+ !py::isinstance<py::float_>(inner_list_rot[2]) ||
104
+ !py::isinstance<py::float_>(inner_list_rot[3])
105
+ )
106
+ {
107
+ throw std::runtime_error("correct_motion python bindings: Expected joint_ref_rotations to be a list of lists, length 4, float values");
108
+ }
109
+
110
+ defaultPose[i].SetTranslation(Math::Vector(
111
+ inner_list[0].cast<float>(),
112
+ inner_list[1].cast<float>(),
113
+ inner_list[2].cast<float>()));
114
+ defaultPose[i].SetRotation(Math::Quaternion(
115
+ inner_list_rot[0].cast<float>(),
116
+ inner_list_rot[1].cast<float>(),
117
+ inner_list_rot[2].cast<float>(),
118
+ inner_list_rot[3].cast<float>()
119
+ ));
120
+ }
121
+
122
+ std::vector<int> joint_parents_vec(joint_parents.size());
123
+ for (size_t i = 0; i < joint_parents.size(); ++i)
124
+ {
125
+ if (!py::isinstance<py::int_>(joint_parents[i]))
126
+ {
127
+ throw std::runtime_error("correct_motion python bindings: Expected joint_parents to be a list of ints");
128
+ }
129
+ joint_parents_vec[i] = joint_parents[i].cast<int>();
130
+ if (joint_parents_vec[i] >= (int)joint_parents.size())
131
+ {
132
+ throw std::runtime_error("correct_motion python bindings: joint_parents must be a list of ints, and all values must be less than the number of joints");
133
+ }
134
+ }
135
+
136
+ size_t num_joints = defaultPose.size();
137
+ size_t gen_length = fullPoseMask.size();
138
+
139
+ if(
140
+ leftHandMask.size() != (int)gen_length ||
141
+ rightHandMask.size() != (int)gen_length ||
142
+ leftFootMask.size() != (int)gen_length ||
143
+ rightFootMask.size() != (int)gen_length ||
144
+ rootMask.size() != (int)gen_length
145
+ )
146
+ {
147
+ throw std::runtime_error("correct_motion python bindings: all masks must have the same size");
148
+ }
149
+
150
+ if(rootTranslations.size() != 3 * (int)gen_length)
151
+ {
152
+ throw std::runtime_error("correct_motion python bindings: rootTranslations has the wrong size");
153
+ }
154
+ if(jointRotations.size() != 4 * (int)num_joints * (int)gen_length)
155
+ {
156
+ throw std::runtime_error("correct_motion python bindings: jointRotations has the wrong size");
157
+ }
158
+
159
+ if(rootTranslationsTarget.size() != 3 * (int)gen_length)
160
+ {
161
+ throw std::runtime_error("correct_motion python bindings: rootTranslationsTarget has the wrong size");
162
+ }
163
+ if(jointRotationsTarget.size() != 4 * (int)num_joints * (int)gen_length)
164
+ {
165
+ throw std::runtime_error("correct_motion python bindings: jointRotationsTarget has the wrong size");
166
+ }
167
+
168
+ std::vector<Animation::ContactInfo> endEffectorPins(4);
169
+ endEffectorPins[0].jointIndex = left_hand_idx;
170
+ endEffectorPins[0].hintOffset = Math::Vector(0.0f, 0.0f, -0.1f);
171
+
172
+ endEffectorPins[1].jointIndex = right_hand_idx;
173
+ endEffectorPins[1].hintOffset = Math::Vector(0.0f, 0.0f, -0.1f);
174
+
175
+ endEffectorPins[2].jointIndex = left_foot_idx;
176
+ endEffectorPins[2].hintOffset = Math::Vector(0.0f, 0.0f, 0.1f);
177
+
178
+ endEffectorPins[3].jointIndex = right_foot_idx;
179
+ endEffectorPins[3].hintOffset = Math::Vector(0.0f, 0.0f, 0.1f);
180
+
181
+ endEffectorPins[0].contactMask.reserve(gen_length);
182
+ endEffectorPins[1].contactMask.reserve(gen_length);
183
+ endEffectorPins[2].contactMask.reserve(gen_length);
184
+ endEffectorPins[3].contactMask.reserve(gen_length);
185
+ for(size_t i = 0; i < gen_length; ++i)
186
+ {
187
+ endEffectorPins[0].contactMask.push_back((1.0f - fullPoseMask.at(i)) * leftHandMask.at(i));
188
+ endEffectorPins[1].contactMask.push_back((1.0f - fullPoseMask.at(i)) * rightHandMask.at(i));
189
+ endEffectorPins[2].contactMask.push_back((1.0f - fullPoseMask.at(i)) * leftFootMask.at(i));
190
+ endEffectorPins[3].contactMask.push_back((1.0f - fullPoseMask.at(i)) * rightFootMask.at(i));
191
+ }
192
+
193
+ std::vector<Animation::ContactInfo> contactInfo(2);
194
+
195
+ auto footTranslation = Animation::JointLocalToGlobal(
196
+ joint_parents_vec,
197
+ right_foot_idx,
198
+ defaultPose
199
+ ).GetTranslation();
200
+
201
+ contactInfo[0].jointIndex = right_foot_idx;
202
+ contactInfo[0].hintOffset = Math::Vector(0.0f, 0.0f, 0.1f);
203
+ contactInfo[0].minHeight = footTranslation.GetY();
204
+
205
+ footTranslation = Animation::JointLocalToGlobal(
206
+ joint_parents_vec,
207
+ left_foot_idx,
208
+ defaultPose
209
+ ).GetTranslation();
210
+
211
+ contactInfo[1].jointIndex = left_foot_idx;
212
+ contactInfo[1].hintOffset = Math::Vector(0.0f, 0.0f, 0.1f);
213
+ contactInfo[1].minHeight = footTranslation.GetY();
214
+
215
+ auto& rContacts = contactInfo[0].contactMask;
216
+ auto& lContacts = contactInfo[1].contactMask;
217
+
218
+ rContacts.resize(fullPoseMask.size());
219
+ lContacts.resize(fullPoseMask.size());
220
+ for (int i = 0; i < fullPoseMask.size(); ++i)
221
+ {
222
+ // don't flag it as a contact if it's been masked:
223
+ rContacts[i] = rightFootMask.at(i) ? 0 : contacts.at(4 * i + 2);
224
+ lContacts[i] = leftFootMask.at(i) ? 0 : contacts.at(4 * i + 0);
225
+
226
+ // Flag the heel as a contact if the toe is a contact:
227
+ rContacts[i] = std::min((rightFootMask.at(i) ? 0 : contacts.at(4 * i + 3)) + rContacts[i], 1.0f);
228
+ lContacts[i] = std::min((leftFootMask.at(i) ? 0 : contacts.at(4 * i + 1)) + lContacts[i], 1.0f);
229
+ }
230
+
231
+ int left_toe_idx = -1;
232
+ int right_toe_idx = -1;
233
+ for(int i = 0; i < num_joints; ++i)
234
+ {
235
+ if(joint_parents_vec[i] == left_foot_idx)
236
+ {
237
+ left_toe_idx = i;
238
+ }
239
+ if(joint_parents_vec[i] == right_foot_idx)
240
+ {
241
+ right_toe_idx = i;
242
+ }
243
+ }
244
+
245
+ if(left_toe_idx != -1 && right_toe_idx != -1)
246
+ {
247
+ auto toeTranslation = Animation::JointLocalToGlobal(
248
+ joint_parents_vec,
249
+ right_toe_idx,
250
+ defaultPose
251
+ ).GetTranslation();
252
+
253
+ contactInfo.resize(4);
254
+ contactInfo[2].jointIndex = right_toe_idx;
255
+ contactInfo[2].contactType = Animation::kOneBone;
256
+ contactInfo[2].minHeight = toeTranslation.GetY();
257
+
258
+ contactInfo[3].jointIndex = left_toe_idx;
259
+ contactInfo[3].contactType = Animation::kOneBone;
260
+ contactInfo[3].minHeight = toeTranslation.GetY();
261
+
262
+ auto& rToeContacts = contactInfo[2].contactMask;
263
+ auto& lToeContacts = contactInfo[3].contactMask;
264
+
265
+ // fill up the ankle contacts:
266
+ rToeContacts.resize(fullPoseMask.size());
267
+ lToeContacts.resize(fullPoseMask.size());
268
+
269
+ for (int i = 0; i < fullPoseMask.size(); ++i)
270
+ {
271
+ // don't flag it as a contact if it's been masked:
272
+ rToeContacts[i] = rightFootMask.at(i) ? 0 : contacts.at(4 * i + 3);
273
+ lToeContacts[i] = leftFootMask.at(i) ? 0 : contacts.at(4 * i + 1);
274
+ }
275
+ }
276
+
277
+
278
+ auto setTransforms = [gen_length, num_joints](
279
+ std::vector< std::vector<Math::Transform> > &poses,
280
+ const py::array_t<float> &rootTranslations,
281
+ const py::array_t<float> &jointRotations
282
+ )
283
+ {
284
+ for (size_t f = 0; f < gen_length; ++f)
285
+ {
286
+ poses[f][0].SetTranslation({
287
+ strip_nan_inf(rootTranslations.at(3*f+0)),
288
+ strip_nan_inf(rootTranslations.at(3*f+1)),
289
+ strip_nan_inf(rootTranslations.at(3*f+2))
290
+ });
291
+ }
292
+
293
+ for (size_t f = 0; f < gen_length; ++f)
294
+ {
295
+ for (size_t j = 0; j < num_joints; ++j)
296
+ {
297
+ // x y z w order:
298
+ Math::Quaternion q(
299
+ strip_nan_inf(jointRotations.at(4 * (num_joints * f + j) + 1)),
300
+ strip_nan_inf(jointRotations.at(4 * (num_joints * f + j) + 2)),
301
+ strip_nan_inf(jointRotations.at(4 * (num_joints * f + j) + 3)),
302
+ strip_nan_inf(jointRotations.at(4 * (num_joints * f + j) + 0))
303
+ );
304
+ q.Normalize();
305
+ poses[f][j].SetRotation(q);
306
+ }
307
+ }
308
+ };
309
+
310
+ std::vector< std::vector<Math::Transform> > posesFixed(gen_length, defaultPose);
311
+ setTransforms(posesFixed, rootTranslations, jointRotations);
312
+
313
+ std::vector< std::vector<Math::Transform> > posesTarget(gen_length, defaultPose);
314
+ setTransforms(posesTarget, rootTranslationsTarget, jointRotationsTarget);
315
+
316
+ std::vector<float> fullPoseMask_vec;
317
+ std::vector<float> rootMask_vec;
318
+ for (size_t f = 0; f < gen_length; ++f)
319
+ {
320
+ fullPoseMask_vec.push_back(fullPoseMask.at(f));
321
+ rootMask_vec.push_back(rootMask.at(f));
322
+ }
323
+
324
+ Animation::CorrectMotion(
325
+ posesFixed,
326
+ posesTarget,
327
+ fullPoseMask_vec,
328
+ rootMask_vec,
329
+ contactInfo,
330
+ endEffectorPins,
331
+ joint_parents_vec,
332
+ defaultPose,
333
+ contact_threshold,
334
+ root_margin,
335
+ has_double_ankle_joints
336
+ );
337
+
338
+ for (size_t f = 0; f < gen_length; ++f)
339
+ {
340
+ auto t = posesFixed[f][0].GetTranslation();
341
+ rootTranslations.mutable_at(3*f+0) = t.GetX();
342
+ rootTranslations.mutable_at(3*f+1) = t.GetY();
343
+ rootTranslations.mutable_at(3*f+2) = t.GetZ();
344
+ }
345
+
346
+ for (size_t f = 0; f < gen_length; ++f)
347
+ {
348
+ for (size_t j = 0; j < num_joints; ++j)
349
+ {
350
+ auto q = posesFixed[f][j].GetRotation();
351
+ // w x y z order
352
+ jointRotations.mutable_at(4 * (num_joints * f + j) + 0) = ((float*)&q)[3];
353
+ jointRotations.mutable_at(4 * (num_joints * f + j) + 1) = ((float*)&q)[0];
354
+ jointRotations.mutable_at(4 * (num_joints * f + j) + 2) = ((float*)&q)[1];
355
+ jointRotations.mutable_at(4 * (num_joints * f + j) + 3) = ((float*)&q)[2];
356
+ }
357
+ }
358
+
359
+ }
360
+
361
+ PYBIND11_MODULE(_motion_correction, m) {
362
+ m.doc() = "Motion Correction Python bindings";
363
+ m.def("correct_motion", &correct_motion);
364
+ }
MotionCorrection/src/cpp/Compiler.h ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ // Compiler specific defines
9
+
10
+ // Finds the compiler type and version.
11
+ #if defined(__clang__)
12
+ # define COMPILER_CLANG
13
+ #elif defined(__GNUC__) // Check after Clang, as Clang defines this too
14
+ # define COMPILER_GNUC
15
+ #elif defined(_MSC_VER) // Check after Clang, since we could be building with either within VS
16
+ # define COMPILER_MSVC
17
+ #else
18
+ # pragma error "Unknown compiler. "
19
+ #endif
20
+
21
+ #if defined(COMPILER_MSVC)
22
+ #define FORCE_INLINE __forceinline
23
+ #elif defined(COMPILER_GNUC)
24
+ #define FORCE_INLINE inline __attribute__((always_inline))
25
+ #endif
MotionCorrection/src/cpp/Debug.h ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Platform.h"
9
+
10
+ #define ASSERT( cond ) do { if( !(cond) ) { DEBUG_BREAK(); } } while( 0 )
11
+ #define HALT() { DEBUG_BREAK(); }
12
+ #define UNIMPLEMENTED_FUNCTION() { DEBUG_BREAK(); }
13
+ #define UNREACHABLE_CODE() { DEBUG_BREAK(); }
MotionCorrection/src/cpp/Math/Constants.h ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include <limits>
9
+
10
+ // Mathematical constants
11
+
12
+ namespace Math
13
+ {
14
+ static constexpr float const Epsilon = 1.0e-06f;
15
+ static constexpr float const LargeEpsilon = 1.0e-04f;
16
+ static constexpr float const HugeEpsilon = 1.0e-02f;
17
+ static constexpr float const Pi = 3.141592654f;
18
+ static constexpr float const TwoPi = 6.283185307f;
19
+ static constexpr float const OneDivPi = 0.318309886f;
20
+ static constexpr float const OneDivTwoPi = 0.159154943f;
21
+ static constexpr float const PiDivTwo = 1.570796327f;
22
+ static constexpr float const PiDivFour = 0.785398163f;
23
+
24
+ static constexpr float const SqrtTwo = 1.4142135623730950488016887242097f;
25
+ static constexpr float const OneDivSqrtTwo = 1.0f / SqrtTwo;
26
+
27
+ static constexpr float const DegreesToRadians = 0.0174532925f;
28
+ static constexpr float const RadiansToDegrees = 57.2957795f;
29
+
30
+ static constexpr float const Infinity = std::numeric_limits<float>::infinity();
31
+ static constexpr float const QNaN = std::numeric_limits<float>::quiet_NaN();
32
+ }
MotionCorrection/src/cpp/Math/Matrix.cpp ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "Matrix.h"
7
+
8
+ #include <cfloat>
9
+
10
+ using namespace Math;
11
+
12
+ namespace
13
+ {
14
+ static bool CheckForZeroScaleInRow(float scale, const Vector& row)
15
+ {
16
+ float const absScale = Math::Abs(scale);
17
+
18
+ for (int i = 0; i < 3; i++)
19
+ {
20
+ if (absScale < 1 && Math::Abs(row[i]) >= FLT_MAX * absScale)
21
+ {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ static bool ExtractAndRemoveScalingAndShear(Matrix& matrix, Vector& scale, Vector& shear)
30
+ {
31
+ scale = Vector::Zero;
32
+ shear = Vector::Zero;
33
+
34
+ Float3 scaleValues = Float3::Zero;
35
+ Float3 shearValues = Float3::Zero;
36
+
37
+ // This implementation follows the technique described in the paper by
38
+ // Spencer W. Thomas in the Graphics Gems II article: "Decomposing a
39
+ // Matrix into Simple Transformations", p. 320.
40
+
41
+ Vector row[3];
42
+ row[0] = Vector(matrix[0][0], matrix[0][1], matrix[0][2]);
43
+ row[1] = Vector(matrix[1][0], matrix[1][1], matrix[1][2]);
44
+ row[2] = Vector(matrix[2][0], matrix[2][1], matrix[2][2]);
45
+
46
+ float maxVal = 0;
47
+ for (int i = 0; i < 3; i++)
48
+ {
49
+ for (int j = 0; j < 3; j++)
50
+ {
51
+ if (Math::Abs(row[i][j]) > maxVal)
52
+ {
53
+ maxVal = Math::Abs(row[i][j]);
54
+ }
55
+ }
56
+ }
57
+
58
+ // We normalize the 3x3 matrix here.
59
+ // It was noticed that this can improve numerical stability significantly,
60
+ // especially when many of the upper 3x3 matrix's coefficients are very
61
+ // close to zero; we correct for this step at the end by multiplying the
62
+ // scaling factors by maxVal at the end (shear and rotation are not
63
+ // affected by the normalization).
64
+
65
+ if (maxVal != 0)
66
+ {
67
+ for (int i = 0; i < 3; i++)
68
+ {
69
+ if (!CheckForZeroScaleInRow(maxVal, row[i]))
70
+ {
71
+ return false;
72
+ }
73
+ else
74
+ {
75
+ row[i] /= maxVal;
76
+ }
77
+ }
78
+ }
79
+
80
+ // Compute X scale factor.
81
+ scaleValues.m_x = row[0].Length3().ToFloat();
82
+ if (!CheckForZeroScaleInRow(scaleValues.m_x, row[0]))
83
+ {
84
+ return false;
85
+ }
86
+
87
+ // Normalize first row.
88
+ row[0] /= scaleValues.m_x;
89
+
90
+ // An XY shear factor will shear the X coord. as the Y coord. changes.
91
+ // There are 6 combinations (XY, XZ, YZ, YX, ZX, ZY), although we only
92
+ // extract the first 3 because we can effect the last 3 by shearing in
93
+ // XY, XZ, YZ combined rotations and scales.
94
+ //
95
+ // shear matrix < 1, YX, ZX, 0,
96
+ // XY, 1, ZY, 0,
97
+ // XZ, YZ, 1, 0,
98
+ // 0, 0, 0, 1 >
99
+
100
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
101
+ shearValues[0] = Vector::Dot3(row[0], row[1]).ToFloat();
102
+ row[1] -= row[0] * shearValues[0];
103
+
104
+ // Now, compute Y scale.
105
+ scaleValues.m_y = row[1].Length3().ToFloat();
106
+ if (!CheckForZeroScaleInRow(scaleValues.m_y, row[1]))
107
+ {
108
+ return false;
109
+ }
110
+
111
+ // Normalize 2nd row and correct the XY shear factor for Y scaling.
112
+ row[1] /= scaleValues.m_y;
113
+ shearValues[0] /= scaleValues.m_y;
114
+
115
+ // Compute XZ and YZ shears, orthogonalize 3rd row.
116
+ shearValues[1] = Vector::Dot3(row[0], row[2]).ToFloat();
117
+ row[2] -= row[0] * shearValues[1];
118
+ shearValues[2] = Vector::Dot3(row[1], row[2]).ToFloat();
119
+ row[2] -= row[1] * shearValues[2];
120
+
121
+ // Next, get Z scale.
122
+ scaleValues.m_z = row[2].Length3().ToFloat();
123
+ if (!CheckForZeroScaleInRow(scaleValues.m_z, row[2]))
124
+ {
125
+ return false;
126
+ }
127
+
128
+ // Normalize 3rd row and correct the XZ and YZ shear factors for Z scaling.
129
+ row[2] /= scaleValues.m_z;
130
+ shearValues[1] /= scaleValues.m_z;
131
+ shearValues[2] /= scaleValues.m_z;
132
+
133
+ // At this point, the upper 3x3 matrix in mat is orthonormal.
134
+ // Check for a coordinate system flip. If the determinant
135
+ // is less than zero, then negate the matrix and the scaling factors.
136
+ if (Vector::Dot3(row[0], Vector::Cross3(row[1], row[2])).ToFloat() < 0)
137
+ {
138
+ for (int i = 0; i < 3; i++)
139
+ {
140
+ scaleValues[i] *= -1;
141
+ row[i] *= -1;
142
+ }
143
+ }
144
+
145
+ // Copy over the orthonormal rows into the returned matrix.
146
+ // The upper 3x3 matrix in mat is now a rotation matrix.
147
+ for (int i = 0; i < 3; i++)
148
+ {
149
+ matrix[i].SetX(row[i][0]);
150
+ matrix[i].SetY(row[i][1]);
151
+ matrix[i].SetZ(row[i][2]);
152
+ }
153
+
154
+ // Correct the scaling factors for the normalization step that we
155
+ // performed above; shear and rotation are not affected by the
156
+ // normalization.
157
+ scaleValues *= maxVal;
158
+
159
+ scale = Vector(scaleValues);
160
+ shear = Vector(shearValues);
161
+
162
+ return true;
163
+ }
164
+ }
165
+
166
+ namespace Math
167
+ {
168
+ Matrix const Matrix::Identity(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
169
+
170
+ Matrix::Matrix(float v00, float v01, float v02, float v03, float v10, float v11, float v12, float v13, float v20, float v21, float v22, float v23, float v30, float v31, float v32, float v33)
171
+ {
172
+ m_rows[0] = Vector(v00, v01, v02, v03);
173
+ m_rows[1] = Vector(v10, v11, v12, v13);
174
+ m_rows[2] = Vector(v20, v21, v22, v23);
175
+ m_rows[3] = Vector(v30, v31, v32, v33);
176
+ }
177
+
178
+ Matrix::Matrix(float values[16])
179
+ {
180
+ m_rows[0] = Vector(values[0], values[1], values[2], values[3]);
181
+ m_rows[1] = Vector(values[4], values[5], values[6], values[7]);
182
+ m_rows[2] = Vector(values[8], values[9], values[10], values[11]);
183
+ m_rows[3] = Vector(values[12], values[13], values[14], values[15]);
184
+ }
185
+
186
+ Matrix::Matrix(const Vector& xAxis, const Vector& yAxis, const Vector& zAxis)
187
+ {
188
+ m_rows[0] = xAxis;
189
+ m_rows[1] = yAxis;
190
+ m_rows[2] = zAxis;
191
+ m_rows[3] = Vector::UnitW;
192
+ }
193
+
194
+ Matrix::Matrix(const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector& translation)
195
+ {
196
+ m_rows[0] = xAxis;
197
+ m_rows[1] = yAxis;
198
+ m_rows[2] = zAxis;
199
+ m_rows[3] = translation.GetWithW1();
200
+ }
201
+
202
+ Matrix::Matrix(const EulerAngles& eulerAngles, const Vector translation)
203
+ {
204
+ float cx, cy, cz, sx, sy, sz, czsx, cxcz, sysz;
205
+
206
+ sx = sinf((float)eulerAngles.m_x); cx = cosf((float)eulerAngles.m_x);
207
+ sy = sinf((float)eulerAngles.m_y); cy = cosf((float)eulerAngles.m_y);
208
+ sz = sinf((float)eulerAngles.m_z); cz = cosf((float)eulerAngles.m_z);
209
+
210
+ czsx = cz * sx;
211
+ cxcz = cx * cz;
212
+ sysz = sy * sz;
213
+
214
+ // Order is XYZ
215
+ m_values[0][0] = cy * cz;
216
+ m_values[0][1] = cy * sz;
217
+ m_values[0][2] = -sy;
218
+ m_values[1][0] = czsx * sy - cx * sz;
219
+ m_values[1][1] = cxcz + sx * sysz;
220
+ m_values[1][2] = cy * sx;
221
+ m_values[2][0] = cxcz * sy + sx * sz;
222
+ m_values[2][1] = -czsx + cx * sysz;
223
+ m_values[2][2] = cx * cy;
224
+ m_values[0][3] = 0.0f;
225
+ m_values[1][3] = 0.0f;
226
+ m_values[2][3] = 0.0f;
227
+
228
+ // Translation
229
+ m_rows[3] = translation.GetWithW1();
230
+ }
231
+
232
+ EulerAngles Matrix::ToEulerAngles() const
233
+ {
234
+ EulerAngles result;
235
+
236
+ result.m_x = Radians(Math::ATan2(m_values[1][2], m_values[2][2]));
237
+
238
+ float const c2 = Math::Sqrt((m_values[0][0] * m_values[0][0]) + (m_values[0][1] * m_values[0][1]));
239
+ result.m_y = Radians(Math::ATan2(-m_values[0][2], c2));
240
+
241
+ float const s1 = Math::Sin((float)result.m_x);
242
+ float const c1 = Math::Cos((float)result.m_x);
243
+ result.m_z = Radians(Math::ATan2((s1 * m_values[2][0]) - (c1 * m_values[1][0]), (c1 * m_values[1][1]) - (s1 * m_values[2][1])));
244
+
245
+ return result;
246
+ }
247
+
248
+ bool Matrix::Decompose(Quaternion& outRotation, Vector& outTranslation, Vector& outScale) const
249
+ {
250
+ Matrix copy = *this;
251
+ Vector shr = Vector::Zero;
252
+ outScale = Vector::Zero;
253
+
254
+ // Extract and remove scale and shear from matrix
255
+ if (ExtractAndRemoveScalingAndShear(copy, outScale, shr))
256
+ {
257
+ // Extract rotation and translation from unscaled matrix
258
+ outRotation = copy.GetRotation();
259
+ outTranslation = copy.GetTranslation().GetWithW0();
260
+ return true;
261
+ }
262
+
263
+ return false;
264
+ }
265
+
266
+ Vector Matrix::GetScale() const
267
+ {
268
+ Matrix copy = *this;
269
+ Vector scale = Vector::Zero, shear;
270
+ if (!ExtractAndRemoveScalingAndShear(copy, scale, shear))
271
+ {
272
+ float const lengthX = m_rows[0].Length3().ToFloat();
273
+ float const lengthY = m_rows[1].Length3().ToFloat();
274
+ float const lengthZ = m_rows[2].Length3().ToFloat();
275
+ scale = Vector(lengthX, lengthY, lengthZ, 0.0f);
276
+ }
277
+
278
+ return scale;
279
+ }
280
+
281
+ Matrix& Matrix::SetScale(const Vector& newScale)
282
+ {
283
+ Vector scale, shear;
284
+ bool result = ExtractAndRemoveScalingAndShear(*this, scale, shear);
285
+
286
+ // Cannot set scale on matrix that contains zero-scale
287
+ ASSERT(result);
288
+
289
+ m_rows[0] = m_rows[0] * newScale.GetSplatX();
290
+ m_rows[1] = m_rows[1] * newScale.GetSplatY();
291
+ m_rows[2] = m_rows[2] * newScale.GetSplatZ();
292
+ return *this;
293
+ }
294
+
295
+ Matrix& Matrix::RemoveScale()
296
+ {
297
+ Vector scale, shear;
298
+ bool result = ExtractAndRemoveScalingAndShear(*this, scale, shear);
299
+
300
+ // Cannot remove zero scale from matrix
301
+ ASSERT(result);
302
+
303
+ return *this;
304
+ }
305
+ }
MotionCorrection/src/cpp/Math/Matrix.h ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Vector.h"
9
+ #include "Quaternion.h"
10
+
11
+ enum class CoordinateSpace : uint8_t
12
+ {
13
+ World,
14
+ Local,
15
+ };
16
+
17
+ //
18
+ // Matrices are Row-Major
19
+ // Multiplication order is right to left
20
+ // ObjectWorldTransform = LocalObjectTransform * WorldTransform
21
+ //
22
+
23
+ namespace Math
24
+ {
25
+ class alignas(16) Matrix
26
+ {
27
+ public:
28
+
29
+ static Matrix const Identity;
30
+
31
+ public:
32
+
33
+ static Matrix FromRotation(const Quaternion& rotation);
34
+ static Matrix FromTranslation(const Vector& translation);
35
+ static Matrix FromScale(const Vector& scale);
36
+ static Matrix FromUniformScale(float uniformScale);
37
+ static Matrix FromTranslationAndScale(const Vector& translation, const Vector& scale);
38
+ static Matrix FromRotationBetweenVectors(const Vector sourceVector, const Vector targetVector);
39
+
40
+ public:
41
+
42
+ explicit Matrix();
43
+ explicit Matrix(NoInit_t);
44
+ explicit Matrix(ZeroInit_t);
45
+ explicit Matrix(float v00, float v01, float v02, float v03,
46
+ float v10, float v11, float v12, float v13,
47
+ float v20, float v21, float v22, float v23,
48
+ float v30, float v31, float v32, float v33);
49
+ explicit Matrix(float values[16]);
50
+ explicit Matrix(Vector const& xAxis, Vector const& yAxis, Vector const& zAxis);
51
+ explicit Matrix(Vector const& xAxis, Vector const& yAxis, Vector const& zAxis, Vector const& translation);
52
+
53
+ Matrix(const Vector axis, Radians angleRadians);
54
+ Matrix(const AxisAngle axisAngle);
55
+
56
+ explicit Matrix(const Quaternion& rotation);
57
+ explicit Matrix(const Quaternion& rotation, const Vector& translation, const Vector& scale = Vector::One);
58
+ explicit Matrix(const Quaternion& rotation, const Vector& translation, float scale = 1.0f);
59
+ explicit Matrix(const EulerAngles& eulerAngles, const Vector translation = Vector::UnitW);
60
+
61
+ EulerAngles ToEulerAngles() const;
62
+
63
+ float* AsFloatArray();
64
+ const float* AsFloatArray() const;
65
+ const Vector& GetRow(uint32_t row) const;
66
+
67
+ const Vector& GetAxisX() const;
68
+ const Vector& GetAxisY() const;
69
+ const Vector& GetAxisZ() const;
70
+
71
+ void SetAxisX(const Vector& xAxis);
72
+ void SetAxisY(const Vector& yAxis);
73
+ void SetAxisZ(const Vector& zAxis);
74
+
75
+ Float3 GetForwardVector() const;
76
+ Float3 GetRightVector() const;
77
+ Float3 GetUpVector() const;
78
+
79
+ Vector GetUnitAxisX() const;
80
+ Vector GetUnitAxisY() const;
81
+ Vector GetUnitAxisZ() const;
82
+
83
+ bool IsIdentity() const;
84
+ bool IsOrthogonal() const;
85
+ bool IsOrthonormal() const;
86
+
87
+ bool Decompose(Quaternion& outRotation, Vector& outTranslation, Vector& outScale) const;
88
+
89
+ Matrix& Transpose();
90
+ Matrix GetTransposed() const;
91
+
92
+ Matrix& Invert();
93
+ Matrix GetInverse() const;
94
+
95
+ Vector GetDeterminant() const;
96
+ float GetDeterminantAsFloat() const;
97
+
98
+ Vector GetTranslation() const;
99
+ const Vector& GetTranslationWithW() const;
100
+ Matrix& SetTranslation(Vector const& v);
101
+ Matrix& SetTranslation(Float3 const& v);
102
+ Matrix& SetTranslation(Float4 const& v);
103
+
104
+ Quaternion GetRotation() const;
105
+
106
+ Matrix& SetRotation(const Matrix& rotation);
107
+ Matrix& SetRotation(const Quaternion& rotation);
108
+
109
+ Matrix& SetRotationMaintainingScale(const Matrix& rotation);
110
+ Matrix& SetRotationMaintainingScale(const Quaternion& rotation);
111
+
112
+ Vector GetScale() const;
113
+
114
+ Matrix& RemoveScale();
115
+ Matrix& SetScale(const Vector& scale);
116
+ Matrix& SetScale(float uniformScale);
117
+
118
+ Matrix& RemoveScaleFast();
119
+ Matrix& SetScaleFast(const Vector& scale);
120
+ Matrix& SetScaleFast(float uniformScale);
121
+
122
+ //
123
+ // Operators
124
+ //
125
+
126
+ // Applies rotation and scale to a vector and returns a result with the W = 0
127
+ Vector RotateVector(const Vector& vector) const;
128
+
129
+ // Applies rotation and scale to a vector and returns a result with the W = 0
130
+ Vector TransformNormal(const Vector& vector) const;
131
+
132
+ // Applies the transformation to a given point and ensures the resulting W = 1
133
+ Vector TransformPoint(const Vector& point) const;
134
+
135
+ // Applies the transformation to a vector ignoring the W value.
136
+ // Same as TransformPoint with the result W left unchanged
137
+ Vector TransformVector3(const Vector& vector) const;
138
+
139
+ // Applies the transformation to a given vector with the result W left unchanged
140
+ Vector TransformVector4(const Vector& vector) const;
141
+
142
+ Vector& operator[](uint32_t i);
143
+ const Vector operator[](uint32_t i) const;
144
+
145
+ Matrix operator*(const Matrix& rhs) const;
146
+ Matrix& operator*=(const Matrix& rhs);
147
+
148
+ Matrix operator*(const Quaternion& rhs) const;
149
+ Matrix operator*=(const Quaternion& rhs);
150
+
151
+ bool operator==(const Matrix& rhs) const;
152
+
153
+ public:
154
+
155
+ union
156
+ {
157
+ Vector m_rows[4];
158
+ float m_values[4][4];
159
+ };
160
+ };
161
+ }
162
+
163
+ #include "Matrix.inl"
MotionCorrection/src/cpp/Math/Matrix.inl ADDED
@@ -0,0 +1,802 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include <cstring>
9
+
10
+ #include "Matrix.h"
11
+
12
+ namespace Math
13
+ {
14
+ inline Matrix Matrix::FromRotation(const Quaternion& rotation)
15
+ {
16
+ return Matrix(rotation);
17
+ }
18
+
19
+ inline Matrix Matrix::FromTranslation(const Vector& translation)
20
+ {
21
+ Matrix M;
22
+ M.m_rows[0] = Vector::UnitX;
23
+ M.m_rows[1] = Vector::UnitY;
24
+ M.m_rows[2] = Vector::UnitZ;
25
+ M.m_rows[3] = translation.GetWithW1();
26
+ return M;
27
+ }
28
+
29
+ inline Matrix Matrix::FromScale(const Vector& scale)
30
+ {
31
+ Matrix M;
32
+ M.m_rows[0] = _mm_and_ps(scale, SIMD::g_maskX000);
33
+ M.m_rows[1] = _mm_and_ps(scale, SIMD::g_mask0Y00);
34
+ M.m_rows[2] = _mm_and_ps(scale, SIMD::g_mask00Z0);
35
+ M.m_rows[3] = Vector::UnitW;
36
+ return M;
37
+ }
38
+
39
+ inline Matrix Matrix::FromUniformScale(float uniformScale)
40
+ {
41
+ Matrix M;
42
+ M.m_rows[0] = _mm_set_ps(0, 0, 0, uniformScale);
43
+ M.m_rows[1] = _mm_set_ps(0, 0, uniformScale, 0);
44
+ M.m_rows[2] = _mm_set_ps(0, uniformScale, 0, 0);
45
+ M.m_rows[3] = Vector::UnitW;
46
+ return M;
47
+ }
48
+
49
+ inline Matrix Matrix::FromTranslationAndScale(const Vector& translation, const Vector& scale)
50
+ {
51
+ Matrix M;
52
+ M.m_rows[0] = _mm_and_ps(scale, SIMD::g_maskX000);
53
+ M.m_rows[1] = _mm_and_ps(scale, SIMD::g_mask0Y00);
54
+ M.m_rows[2] = _mm_and_ps(scale, SIMD::g_mask00Z0);
55
+ M.m_rows[3] = translation.GetWithW1();
56
+ return M;
57
+ }
58
+
59
+ inline Matrix Matrix::FromRotationBetweenVectors(Vector const sourceVector, Vector const targetVector)
60
+ {
61
+ return Matrix(Quaternion::FromRotationBetweenNormalizedVectors(sourceVector, targetVector));
62
+ }
63
+
64
+ inline Matrix::Matrix()
65
+ {
66
+ memcpy(this, &Matrix::Identity, sizeof(Matrix));
67
+ }
68
+
69
+ inline Matrix::Matrix(NoInit_t)
70
+ {
71
+ }
72
+
73
+ inline Matrix::Matrix(ZeroInit_t)
74
+ {
75
+ memset(this, 0, sizeof(Matrix));
76
+ }
77
+
78
+ inline Matrix::Matrix(const Vector axis, Radians angleRadians)
79
+ {
80
+ Vector normal = axis.GetNormalized3();
81
+
82
+ Vector C0, C1;
83
+ Vector::SinCos(C0, C1, Vector((float)angleRadians));
84
+ Vector C2 = Vector::One - C1;
85
+
86
+ __m128 N0 = _mm_shuffle_ps(normal, normal, _MM_SHUFFLE(3, 0, 2, 1));
87
+ __m128 N1 = _mm_shuffle_ps(normal, normal, _MM_SHUFFLE(3, 1, 0, 2));
88
+
89
+ __m128 V0 = _mm_mul_ps(C2, N0);
90
+ V0 = _mm_mul_ps(V0, N1);
91
+
92
+ __m128 R0 = _mm_mul_ps(C2, normal);
93
+ R0 = _mm_mul_ps(R0, normal);
94
+ R0 = _mm_add_ps(R0, C1);
95
+
96
+ __m128 R1 = _mm_mul_ps(C0, normal);
97
+ R1 = _mm_add_ps(R1, V0);
98
+ __m128 R2 = _mm_mul_ps(C0, normal);
99
+ R2 = _mm_sub_ps(V0, R2);
100
+
101
+ V0 = _mm_and_ps(R0, SIMD::g_maskXYZ0);
102
+ __m128 V1 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 1, 2, 0));
103
+ V1 = _mm_shuffle_ps(V1, V1, _MM_SHUFFLE(0, 3, 2, 1));
104
+ __m128 V2 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(0, 0, 1, 1));
105
+ V2 = _mm_shuffle_ps(V2, V2, _MM_SHUFFLE(2, 0, 2, 0));
106
+
107
+ R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(1, 0, 3, 0));
108
+ R2 = _mm_shuffle_ps(R2, R2, _MM_SHUFFLE(1, 3, 2, 0));
109
+
110
+ m_rows[0] = R2;
111
+
112
+ R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(3, 2, 3, 1));
113
+ R2 = _mm_shuffle_ps(R2, R2, _MM_SHUFFLE(1, 3, 0, 2));
114
+ m_rows[1] = R2;
115
+
116
+ V2 = _mm_shuffle_ps(V2, V0, _MM_SHUFFLE(3, 2, 1, 0));
117
+ m_rows[2] = V2;
118
+ m_rows[3] = Vector::UnitW;
119
+ }
120
+
121
+ inline Matrix::Matrix(const AxisAngle axisAngle)
122
+ : Matrix(Vector(axisAngle.m_axis), axisAngle.m_angle)
123
+ {
124
+ }
125
+
126
+ inline Matrix::Matrix(const Quaternion& rotation)
127
+ {
128
+ SetRotation(rotation);
129
+ m_rows[3] = Vector::UnitW;
130
+ }
131
+
132
+ inline Matrix::Matrix(const Quaternion& rotation, const Vector& translation, const Vector& scale)
133
+ {
134
+ SetRotation(rotation);
135
+ m_rows[0] = m_rows[0] * scale.GetSplatX();
136
+ m_rows[1] = m_rows[1] * scale.GetSplatY();
137
+ m_rows[2] = m_rows[2] * scale.GetSplatZ();
138
+ m_rows[3] = translation.GetWithW1();
139
+ }
140
+
141
+ inline Matrix::Matrix(const Quaternion& rotation, const Vector& translation, float scale)
142
+ : Matrix(rotation, translation, Vector(scale))
143
+ {
144
+ }
145
+
146
+ inline float* Matrix::AsFloatArray()
147
+ {
148
+ return &m_values[0][0];
149
+ }
150
+
151
+ inline const float* Matrix::AsFloatArray() const
152
+ {
153
+ return &m_values[0][0];
154
+ }
155
+
156
+ inline const Vector& Matrix::GetRow(uint32_t row) const
157
+ {
158
+ return m_rows[row];
159
+ }
160
+
161
+ inline const Vector& Matrix::GetAxisX() const
162
+ {
163
+ return m_rows[0];
164
+ }
165
+
166
+ inline const Vector& Matrix::GetAxisY() const
167
+ {
168
+ return m_rows[1];
169
+ }
170
+
171
+ inline const Vector& Matrix::GetAxisZ() const
172
+ {
173
+ return m_rows[2];
174
+ }
175
+
176
+ inline void Matrix::SetAxisX(const Vector& xAxis)
177
+ {
178
+ m_rows[0] = xAxis;
179
+ }
180
+
181
+ inline void Matrix::SetAxisY(const Vector& yAxis)
182
+ {
183
+ m_rows[1] = yAxis;
184
+ }
185
+
186
+ inline void Matrix::SetAxisZ(const Vector& zAxis)
187
+ {
188
+ m_rows[2] = zAxis;
189
+ }
190
+
191
+
192
+ inline Float3 Matrix::GetForwardVector() const
193
+ {
194
+ return GetAxisZ();
195
+ }
196
+
197
+ inline Float3 Matrix::GetRightVector() const
198
+ {
199
+ return GetAxisX();
200
+ }
201
+
202
+ inline Float3 Matrix::GetUpVector() const
203
+ {
204
+ return GetAxisY();
205
+ }
206
+
207
+ inline Vector Matrix::GetUnitAxisX() const
208
+ {
209
+ return m_rows[0].GetNormalized3();
210
+ }
211
+
212
+ inline Vector Matrix::GetUnitAxisY() const
213
+ {
214
+ return m_rows[1].GetNormalized3();
215
+ }
216
+
217
+ inline Vector Matrix::GetUnitAxisZ() const
218
+ {
219
+ return m_rows[2].GetNormalized3();
220
+ }
221
+
222
+ inline bool Matrix::IsIdentity() const
223
+ {
224
+ __m128 vTemp1 = _mm_cmpeq_ps(m_rows[0], Vector::UnitX);
225
+ __m128 vTemp2 = _mm_cmpeq_ps(m_rows[1], Vector::UnitY);
226
+ __m128 vTemp3 = _mm_cmpeq_ps(m_rows[2], Vector::UnitZ);
227
+ __m128 vTemp4 = _mm_cmpeq_ps(m_rows[3], Vector::UnitW);
228
+ vTemp1 = _mm_and_ps(vTemp1, vTemp2);
229
+ vTemp3 = _mm_and_ps(vTemp3, vTemp4);
230
+ vTemp1 = _mm_and_ps(vTemp1, vTemp3);
231
+ return (_mm_movemask_ps(vTemp1) == 0x0f);
232
+ }
233
+
234
+ inline bool Matrix::IsOrthogonal() const
235
+ {
236
+ Matrix const transpose = GetTransposed();
237
+ Matrix result = *this * transpose;
238
+ return result.IsIdentity();
239
+ }
240
+
241
+ inline bool Matrix::IsOrthonormal() const
242
+ {
243
+ static const Vector three(3);
244
+ auto dotCheck = Vector::Dot3(m_rows[0], m_rows[1]) + Vector::Dot3(m_rows[0], m_rows[2]) + Vector::Dot3(m_rows[1], m_rows[2]);
245
+ auto magnitudeCheck = m_rows[0].LengthSquared3() + m_rows[1].LengthSquared3() + m_rows[2].LengthSquared3();
246
+ auto result = dotCheck + magnitudeCheck;
247
+ return result.IsNearEqual3(three);
248
+ }
249
+
250
+ inline Matrix& Matrix::Transpose()
251
+ {
252
+ __m128 vTemp1 = _mm_shuffle_ps(m_rows[0], m_rows[1], _MM_SHUFFLE(1, 0, 1, 0));
253
+ __m128 vTemp3 = _mm_shuffle_ps(m_rows[0], m_rows[1], _MM_SHUFFLE(3, 2, 3, 2));
254
+ __m128 vTemp2 = _mm_shuffle_ps(m_rows[2], m_rows[3], _MM_SHUFFLE(1, 0, 1, 0));
255
+ __m128 vTemp4 = _mm_shuffle_ps(m_rows[2], m_rows[3], _MM_SHUFFLE(3, 2, 3, 2));
256
+ m_rows[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0));
257
+ m_rows[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1));
258
+ m_rows[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0));
259
+ m_rows[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1));
260
+ return *this;
261
+ }
262
+
263
+ inline Matrix Matrix::GetTransposed() const
264
+ {
265
+ Matrix m = *this;
266
+ m.Transpose();
267
+ return m;
268
+ }
269
+
270
+ inline Matrix& Matrix::Invert()
271
+ {
272
+ Matrix MT = GetTransposed();
273
+ __m128 V00 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[2], _MM_SHUFFLE(1, 1, 0, 0));
274
+ __m128 V10 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[3], _MM_SHUFFLE(3, 2, 3, 2));
275
+ __m128 V01 = _mm_shuffle_ps(MT.m_rows[0], MT.m_rows[0], _MM_SHUFFLE(1, 1, 0, 0));
276
+ __m128 V11 = _mm_shuffle_ps(MT.m_rows[1], MT.m_rows[1], _MM_SHUFFLE(3, 2, 3, 2));
277
+ __m128 V02 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[0], _MM_SHUFFLE(2, 0, 2, 0));
278
+ __m128 V12 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[1], _MM_SHUFFLE(3, 1, 3, 1));
279
+
280
+ __m128 D0 = _mm_mul_ps(V00, V10);
281
+ __m128 D1 = _mm_mul_ps(V01, V11);
282
+ __m128 D2 = _mm_mul_ps(V02, V12);
283
+
284
+ V00 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[2], _MM_SHUFFLE(3, 2, 3, 2));
285
+ V10 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[3], _MM_SHUFFLE(1, 1, 0, 0));
286
+ V01 = _mm_shuffle_ps(MT.m_rows[0], MT.m_rows[0], _MM_SHUFFLE(3, 2, 3, 2));
287
+ V11 = _mm_shuffle_ps(MT.m_rows[1], MT.m_rows[1], _MM_SHUFFLE(1, 1, 0, 0));
288
+ V02 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[0], _MM_SHUFFLE(3, 1, 3, 1));
289
+ V12 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[1], _MM_SHUFFLE(2, 0, 2, 0));
290
+
291
+ V00 = _mm_mul_ps(V00, V10);
292
+ V01 = _mm_mul_ps(V01, V11);
293
+ V02 = _mm_mul_ps(V02, V12);
294
+ D0 = _mm_sub_ps(D0, V00);
295
+ D1 = _mm_sub_ps(D1, V01);
296
+ D2 = _mm_sub_ps(D2, V02);
297
+ // V11 = D0Y,D0W,D2Y,D2Y
298
+ V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 1, 3, 1));
299
+ V00 = _mm_shuffle_ps(MT.m_rows[1], MT.m_rows[1], _MM_SHUFFLE(1, 0, 2, 1));
300
+ V10 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(0, 3, 0, 2));
301
+ V01 = _mm_shuffle_ps(MT.m_rows[0], MT.m_rows[0], _MM_SHUFFLE(0, 1, 0, 2));
302
+ V11 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(2, 1, 2, 1));
303
+ // V13 = D1Y,D1W,D2W,D2W
304
+ __m128 V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 3, 3, 1));
305
+ V02 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[3], _MM_SHUFFLE(1, 0, 2, 1));
306
+ V12 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(0, 3, 0, 2));
307
+ __m128 V03 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[2], _MM_SHUFFLE(0, 1, 0, 2));
308
+ V13 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(2, 1, 2, 1));
309
+
310
+ __m128 C0 = _mm_mul_ps(V00, V10);
311
+ __m128 C2 = _mm_mul_ps(V01, V11);
312
+ __m128 C4 = _mm_mul_ps(V02, V12);
313
+ __m128 C6 = _mm_mul_ps(V03, V13);
314
+
315
+ // V11 = D0X,D0Y,D2X,D2X
316
+ V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(0, 0, 1, 0));
317
+ V00 = _mm_shuffle_ps(MT.m_rows[1], MT.m_rows[1], _MM_SHUFFLE(2, 1, 3, 2));
318
+ V10 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(2, 1, 0, 3));
319
+ V01 = _mm_shuffle_ps(MT.m_rows[0], MT.m_rows[0], _MM_SHUFFLE(1, 3, 2, 3));
320
+ V11 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(0, 2, 1, 2));
321
+ // V13 = D1X,D1Y,D2Z,D2Z
322
+ V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(2, 2, 1, 0));
323
+ V02 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[3], _MM_SHUFFLE(2, 1, 3, 2));
324
+ V12 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(2, 1, 0, 3));
325
+ V03 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[2], _MM_SHUFFLE(1, 3, 2, 3));
326
+ V13 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(0, 2, 1, 2));
327
+
328
+ V00 = _mm_mul_ps(V00, V10);
329
+ V01 = _mm_mul_ps(V01, V11);
330
+ V02 = _mm_mul_ps(V02, V12);
331
+ V03 = _mm_mul_ps(V03, V13);
332
+ C0 = _mm_sub_ps(C0, V00);
333
+ C2 = _mm_sub_ps(C2, V01);
334
+ C4 = _mm_sub_ps(C4, V02);
335
+ C6 = _mm_sub_ps(C6, V03);
336
+
337
+ V00 = _mm_shuffle_ps(MT.m_rows[1], MT.m_rows[1], _MM_SHUFFLE(0, 3, 0, 3));
338
+ // V10 = D0Z,D0Z,D2X,D2Y
339
+ V10 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 2, 2));
340
+ V10 = _mm_shuffle_ps(V10, V10, _MM_SHUFFLE(0, 2, 3, 0));
341
+ V01 = _mm_shuffle_ps(MT.m_rows[0], MT.m_rows[0], _MM_SHUFFLE(2, 0, 3, 1));
342
+ // V11 = D0X,D0W,D2X,D2Y
343
+ V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 3, 0));
344
+ V11 = _mm_shuffle_ps(V11, V11, _MM_SHUFFLE(2, 1, 0, 3));
345
+ V02 = _mm_shuffle_ps(MT.m_rows[3], MT.m_rows[3], _MM_SHUFFLE(0, 3, 0, 3));
346
+ // V12 = D1Z,D1Z,D2Z,D2W
347
+ V12 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 2, 2));
348
+ V12 = _mm_shuffle_ps(V12, V12, _MM_SHUFFLE(0, 2, 3, 0));
349
+ V03 = _mm_shuffle_ps(MT.m_rows[2], MT.m_rows[2], _MM_SHUFFLE(2, 0, 3, 1));
350
+ // V13 = D1X,D1W,D2Z,D2W
351
+ V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 3, 0));
352
+ V13 = _mm_shuffle_ps(V13, V13, _MM_SHUFFLE(2, 1, 0, 3));
353
+
354
+ V00 = _mm_mul_ps(V00, V10);
355
+ V01 = _mm_mul_ps(V01, V11);
356
+ V02 = _mm_mul_ps(V02, V12);
357
+ V03 = _mm_mul_ps(V03, V13);
358
+ __m128 C1 = _mm_sub_ps(C0, V00);
359
+ C0 = _mm_add_ps(C0, V00);
360
+ __m128 C3 = _mm_add_ps(C2, V01);
361
+ C2 = _mm_sub_ps(C2, V01);
362
+ __m128 C5 = _mm_sub_ps(C4, V02);
363
+ C4 = _mm_add_ps(C4, V02);
364
+ __m128 C7 = _mm_add_ps(C6, V03);
365
+ C6 = _mm_sub_ps(C6, V03);
366
+
367
+ C0 = _mm_shuffle_ps(C0, C1, _MM_SHUFFLE(3, 1, 2, 0));
368
+ C2 = _mm_shuffle_ps(C2, C3, _MM_SHUFFLE(3, 1, 2, 0));
369
+ C4 = _mm_shuffle_ps(C4, C5, _MM_SHUFFLE(3, 1, 2, 0));
370
+ C6 = _mm_shuffle_ps(C6, C7, _MM_SHUFFLE(3, 1, 2, 0));
371
+ C0 = _mm_shuffle_ps(C0, C0, _MM_SHUFFLE(3, 1, 2, 0));
372
+ C2 = _mm_shuffle_ps(C2, C2, _MM_SHUFFLE(3, 1, 2, 0));
373
+ C4 = _mm_shuffle_ps(C4, C4, _MM_SHUFFLE(3, 1, 2, 0));
374
+ C6 = _mm_shuffle_ps(C6, C6, _MM_SHUFFLE(3, 1, 2, 0));
375
+
376
+ __m128 vTemp = Vector::Dot4(C0, MT.m_rows[0]);
377
+ vTemp = _mm_div_ps(Vector::One, vTemp);
378
+ m_rows[0] = _mm_mul_ps(C0, vTemp);
379
+ m_rows[1] = _mm_mul_ps(C2, vTemp);
380
+ m_rows[2] = _mm_mul_ps(C4, vTemp);
381
+ m_rows[3] = _mm_mul_ps(C6, vTemp);
382
+ return *this;
383
+ }
384
+
385
+ inline Matrix Matrix::GetInverse() const
386
+ {
387
+ Matrix m = *this;
388
+ m.Invert();
389
+ return m;
390
+ }
391
+
392
+ inline Vector Matrix::GetDeterminant() const
393
+ {
394
+ Vector V0 = m_rows[2].Shuffle(1, 0, 0, 0);
395
+ Vector V1 = m_rows[3].Shuffle(2, 2, 1, 1);
396
+ Vector V2 = m_rows[2].Shuffle(1, 0, 0, 0);
397
+ Vector V3 = m_rows[3].Shuffle(3, 3, 3, 2);
398
+ Vector V4 = m_rows[2].Shuffle(2, 2, 1, 1);
399
+ Vector V5 = m_rows[3].Shuffle(3, 3, 3, 2);
400
+
401
+ Vector P0 = V0 * V1;
402
+ Vector P1 = V2 * V3;
403
+ Vector P2 = V4 * V5;
404
+
405
+ V0 = m_rows[2].Shuffle(2, 2, 1, 1);
406
+ V1 = m_rows[3].Shuffle(1, 0, 0, 0);
407
+ V2 = m_rows[2].Shuffle(3, 3, 3, 2);
408
+ V3 = m_rows[3].Shuffle(1, 0, 0, 0);
409
+ V4 = m_rows[2].Shuffle(3, 3, 3, 2);
410
+ V5 = m_rows[3].Shuffle(2, 2, 1, 1);
411
+
412
+ P0 = Vector::NegativeMultiplySubtract(V0, V1, P0);
413
+ P1 = Vector::NegativeMultiplySubtract(V2, V3, P1);
414
+ P2 = Vector::NegativeMultiplySubtract(V4, V5, P2);
415
+
416
+ V0 = m_rows[1].Shuffle(3, 3, 3, 2);
417
+ V1 = m_rows[1].Shuffle(2, 2, 1, 1);
418
+ V2 = m_rows[1].Shuffle(1, 0, 0, 0);
419
+
420
+ static Vector const Sign(1.0f, -1.0f, 1.0f, -1.0f);
421
+ Vector S = m_rows[0] * Sign;
422
+ Vector R = V0 * P0;
423
+ R = Vector::NegativeMultiplySubtract(V1, P1, R);
424
+ R = Vector::MultiplyAdd(V2, P2, R);
425
+
426
+ return Vector::Dot4(S, R);
427
+ }
428
+
429
+ inline float Matrix::GetDeterminantAsFloat() const
430
+ {
431
+ return GetDeterminant().GetX();
432
+ }
433
+
434
+ inline Vector Matrix::GetTranslation() const
435
+ {
436
+ return m_rows[3].GetWithW0();
437
+ }
438
+
439
+ inline const Vector& Matrix::GetTranslationWithW() const
440
+ {
441
+ return m_rows[3];
442
+ }
443
+
444
+ inline Matrix& Matrix::SetTranslation(const Vector& v)
445
+ {
446
+ m_rows[3] = v.GetWithW1();
447
+ return *this;
448
+ }
449
+
450
+ inline Matrix& Matrix::SetTranslation(const Float3& v)
451
+ {
452
+ m_rows[3] = Vector(v, 1.0f);
453
+ return *this;
454
+ }
455
+
456
+ inline Matrix& Matrix::SetTranslation(const Float4& v)
457
+ {
458
+ m_rows[3] = Vector(v.m_x, v.m_y, v.m_z, 1.0f);
459
+ return *this;
460
+ }
461
+
462
+ inline Quaternion Matrix::GetRotation() const
463
+ {
464
+ // based on RTM: https://github.com/nfrechette/rtm
465
+
466
+ const Vector& axisX = m_rows[0];
467
+ const Vector& axisY = m_rows[1];
468
+ const Vector& axisZ = m_rows[2];
469
+
470
+ // Zero scale is not supported
471
+ if (axisX.IsNearZero4() || axisY.IsNearZero4() || axisZ.IsNearZero4())
472
+ {
473
+ HALT();
474
+ }
475
+
476
+ float const axisX_X = axisX.GetX();
477
+ float const axisY_Y = axisY.GetY();
478
+ float const axisZ_Z = axisZ.GetZ();
479
+
480
+ float const mtx_trace = axisX_X + axisY_Y + axisZ_Z;
481
+ if (mtx_trace > 0.0)
482
+ {
483
+ float const axisX_y = axisX.GetY();
484
+ float const axisX_z = axisX.GetZ();
485
+
486
+ float const axisY_x = axisY.GetX();
487
+ float const axisY_z = axisY.GetZ();
488
+
489
+ float const axisZ_x = axisZ.GetX();
490
+ float const axisZ_y = axisZ.GetY();
491
+
492
+ float const inv_trace = Math::Reciprocal(Math::Sqrt(mtx_trace + 1.0f));
493
+ float const half_inv_trace = inv_trace * 0.5f;
494
+
495
+ float const m_x = (axisY_z - axisZ_y) * half_inv_trace;
496
+ float const m_y = (axisZ_x - axisX_z) * half_inv_trace;
497
+ float const m_z = (axisX_y - axisY_x) * half_inv_trace;
498
+ float const m_w = Math::Reciprocal(inv_trace) * 0.5f;
499
+
500
+ return Quaternion(m_x, m_y, m_z, m_w).GetNormalized();
501
+ }
502
+ else
503
+ {
504
+ // Find the axis with the highest diagonal value
505
+ int32_t axisIdx0 = 0;
506
+ if (axisY_Y > axisX_X)
507
+ {
508
+ axisIdx0 = 1;
509
+ }
510
+
511
+ if (axisZ_Z > m_rows[axisIdx0][axisIdx0])
512
+ {
513
+ axisIdx0 = 2;
514
+ }
515
+
516
+ int32_t const axisIdx1 = (axisIdx0 + 1) % 3;
517
+ int32_t const axisIdx2 = (axisIdx1 + 1) % 3;
518
+
519
+ float const pseudoTrace = 1.0f + m_rows[axisIdx0][axisIdx0] - m_rows[axisIdx1][axisIdx1] - m_rows[axisIdx2][axisIdx2];
520
+ float const inversePseudoTrace = Math::Reciprocal(Math::Sqrt(pseudoTrace));
521
+ float const halfInversePseudoTrace = inversePseudoTrace * 0.5f;
522
+
523
+ Float4 rawQuatValues;
524
+ rawQuatValues[axisIdx0] = Math::Reciprocal(inversePseudoTrace) * 0.5f;
525
+ rawQuatValues[axisIdx1] = halfInversePseudoTrace * (m_rows[axisIdx0][axisIdx1] + m_rows[axisIdx1][axisIdx0]);
526
+ rawQuatValues[axisIdx2] = halfInversePseudoTrace * (m_rows[axisIdx0][axisIdx2] + m_rows[axisIdx2][axisIdx0]);
527
+ rawQuatValues[3] = halfInversePseudoTrace * (m_rows[axisIdx1][axisIdx2] - m_rows[axisIdx2][axisIdx1]);
528
+ return Quaternion(rawQuatValues).GetNormalized();
529
+ }
530
+ }
531
+
532
+ inline Matrix& Matrix::SetRotation(const Matrix& rotation)
533
+ {
534
+ ASSERT(Math::Abs(rotation.GetDeterminant().GetX()) == 1.0f);
535
+ m_rows[0] = rotation.m_rows[0];
536
+ m_rows[1] = rotation.m_rows[1];
537
+ m_rows[2] = rotation.m_rows[2];
538
+ return *this;
539
+ }
540
+
541
+ inline Matrix& Matrix::SetRotation(const Quaternion& rotation)
542
+ {
543
+ static __m128 const constant1110 = { 1.0f, 1.0f, 1.0f, 0.0f };
544
+
545
+ __m128 Q0 = _mm_add_ps(rotation, rotation);
546
+ __m128 Q1 = _mm_mul_ps(rotation, Q0);
547
+
548
+ __m128 V0 = _mm_shuffle_ps(Q1, Q1, _MM_SHUFFLE(3, 0, 0, 1));
549
+ V0 = _mm_and_ps(V0, SIMD::g_maskXYZ0);
550
+ __m128 V1 = _mm_shuffle_ps(Q1, Q1, _MM_SHUFFLE(3, 1, 2, 2));
551
+ V1 = _mm_and_ps(V1, SIMD::g_maskXYZ0);
552
+ __m128 R0 = _mm_sub_ps(constant1110, V0);
553
+ R0 = _mm_sub_ps(R0, V1);
554
+
555
+ V0 = _mm_shuffle_ps(rotation, rotation, _MM_SHUFFLE(3, 1, 0, 0));
556
+ V1 = _mm_shuffle_ps(Q0, Q0, _MM_SHUFFLE(3, 2, 1, 2));
557
+ V0 = _mm_mul_ps(V0, V1);
558
+
559
+ V1 = _mm_shuffle_ps(rotation, rotation, _MM_SHUFFLE(3, 3, 3, 3));
560
+ __m128 V2 = _mm_shuffle_ps(Q0, Q0, _MM_SHUFFLE(3, 0, 2, 1));
561
+ V1 = _mm_mul_ps(V1, V2);
562
+
563
+ __m128 R1 = _mm_add_ps(V0, V1);
564
+ __m128 R2 = _mm_sub_ps(V0, V1);
565
+
566
+ V0 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(1, 0, 2, 1));
567
+ V0 = _mm_shuffle_ps(V0, V0, _MM_SHUFFLE(1, 3, 2, 0));
568
+ V1 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 2, 0, 0));
569
+ V1 = _mm_shuffle_ps(V1, V1, _MM_SHUFFLE(2, 0, 2, 0));
570
+
571
+ Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(1, 0, 3, 0));
572
+ Q1 = _mm_shuffle_ps(Q1, Q1, _MM_SHUFFLE(1, 3, 2, 0));
573
+
574
+ m_rows[0] = Q1;
575
+
576
+ Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(3, 2, 3, 1));
577
+ Q1 = _mm_shuffle_ps(Q1, Q1, _MM_SHUFFLE(1, 3, 0, 2));
578
+ m_rows[1] = Q1;
579
+
580
+ Q1 = _mm_shuffle_ps(V1, R0, _MM_SHUFFLE(3, 2, 1, 0));
581
+ m_rows[2] = Q1;
582
+ return *this;
583
+ }
584
+
585
+ inline Matrix& Matrix::SetRotationMaintainingScale(const Matrix& rotation)
586
+ {
587
+ Vector const scale = GetScale();
588
+ SetRotation(rotation);
589
+ return SetScale(scale);
590
+ }
591
+
592
+ inline Matrix& Matrix::SetRotationMaintainingScale(const Quaternion& rotation)
593
+ {
594
+ Vector const scale = GetScale();
595
+ SetRotation(rotation);
596
+ return SetScale(scale);
597
+ }
598
+
599
+ inline Matrix& Matrix::SetScale(float uniformScale)
600
+ {
601
+ SetScale(Vector(uniformScale));
602
+ return *this;
603
+ }
604
+
605
+ inline Matrix& Matrix::RemoveScaleFast()
606
+ {
607
+ m_rows[0] = m_rows[0].GetNormalized4();
608
+ m_rows[1] = m_rows[1].GetNormalized4();
609
+ m_rows[2] = m_rows[2].GetNormalized4();
610
+ return *this;
611
+ }
612
+
613
+ inline Matrix& Matrix::SetScaleFast(const Vector& scale)
614
+ {
615
+ m_rows[0] = m_rows[0].GetNormalized3() * scale.GetSplatX();
616
+ m_rows[1] = m_rows[1].GetNormalized3() * scale.GetSplatY();
617
+ m_rows[2] = m_rows[2].GetNormalized3() * scale.GetSplatZ();
618
+ return *this;
619
+ }
620
+
621
+ inline Matrix& Matrix::SetScaleFast(float uniformScale)
622
+ {
623
+ SetScaleFast(Vector(uniformScale));
624
+ return *this;
625
+ }
626
+
627
+ inline Vector Matrix::RotateVector(const Vector& vector) const
628
+ {
629
+ Vector const X = vector.GetSplatX();
630
+ Vector const Y = vector.GetSplatY();
631
+ Vector const Z = vector.GetSplatZ();
632
+
633
+ Vector Result = Z * m_rows[2];
634
+ Result = Vector::MultiplyAdd(Y, m_rows[1], Result);
635
+ Result = Vector::MultiplyAdd(X, m_rows[0], Result);
636
+
637
+ return Result;
638
+ }
639
+
640
+ inline Vector Matrix::TransformNormal(const Vector& vector) const
641
+ {
642
+ return RotateVector(vector);
643
+ }
644
+
645
+ inline Vector Matrix::TransformPoint(const Vector& point) const
646
+ {
647
+ Vector const X = point.GetSplatX();
648
+ Vector const Y = point.GetSplatY();
649
+ Vector const Z = point.GetSplatZ();
650
+
651
+ Vector result = Vector::MultiplyAdd(Z, m_rows[2], m_rows[3]);
652
+ result = Vector::MultiplyAdd(Y, m_rows[1], result);
653
+ result = Vector::MultiplyAdd(X, m_rows[0], result);
654
+
655
+ Vector const W = result.GetSplatW();
656
+ return result / W;
657
+ }
658
+
659
+ inline Vector Matrix::TransformVector3(const Vector& V) const
660
+ {
661
+ Vector const X = V.GetSplatX();
662
+ Vector const Y = V.GetSplatY();
663
+ Vector const Z = V.GetSplatZ();
664
+
665
+ Vector result = Vector::MultiplyAdd(Z, m_rows[2], m_rows[3]);
666
+ result = Vector::MultiplyAdd(Y, m_rows[1], result);
667
+ result = Vector::MultiplyAdd(X, m_rows[0], result);
668
+
669
+ return result;
670
+ }
671
+
672
+ inline Vector Matrix::TransformVector4(const Vector& V) const
673
+ {
674
+ // Splat m_x,m_y,m_z and m_w
675
+ Vector vTempX = V.GetSplatX();
676
+ Vector vTempY = V.GetSplatY();
677
+ Vector vTempZ = V.GetSplatZ();
678
+ Vector vTempW = V.GetSplatW();
679
+
680
+ // Mul by the matrix
681
+ vTempX = _mm_mul_ps(vTempX, m_rows[0]);
682
+ vTempY = _mm_mul_ps(vTempY, m_rows[1]);
683
+ vTempZ = _mm_mul_ps(vTempZ, m_rows[2]);
684
+ vTempW = _mm_mul_ps(vTempW, m_rows[3]);
685
+
686
+ // Add them all together
687
+ vTempX = _mm_add_ps(vTempX, vTempY);
688
+ vTempZ = _mm_add_ps(vTempZ, vTempW);
689
+ vTempX = _mm_add_ps(vTempX, vTempZ);
690
+
691
+ return vTempX;
692
+ }
693
+
694
+ inline Vector& Matrix::operator[](uint32_t i)
695
+ {
696
+ ASSERT(i < 4);
697
+ return m_rows[i];
698
+ }
699
+
700
+ inline const Vector Matrix::operator[](uint32_t i) const
701
+ {
702
+ ASSERT(i < 4);
703
+ return m_rows[i];
704
+ }
705
+
706
+ inline Matrix Matrix::operator*(const Matrix& rhs) const
707
+ {
708
+ Matrix result = *this;
709
+ result *= rhs;
710
+ return result;
711
+ }
712
+
713
+ inline Matrix& Matrix::operator*= (const Matrix& rhs)
714
+ {
715
+ Vector vX, vY, vZ, vW;
716
+
717
+ // Use vW to hold the original row
718
+ vW = m_rows[0];
719
+ vX = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(0, 0, 0, 0));
720
+ vY = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(1, 1, 1, 1));
721
+ vZ = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(2, 2, 2, 2));
722
+ vW = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(3, 3, 3, 3));
723
+ vX = _mm_mul_ps(vX, rhs.m_rows[0]);
724
+ vY = _mm_mul_ps(vY, rhs.m_rows[1]);
725
+ vZ = _mm_mul_ps(vZ, rhs.m_rows[2]);
726
+ vW = _mm_mul_ps(vW, rhs.m_rows[3]);
727
+ vX = _mm_add_ps(vX, vZ);
728
+ vY = _mm_add_ps(vY, vW);
729
+ vX = _mm_add_ps(vX, vY);
730
+ m_rows[0] = vX;
731
+
732
+ // Repeat for the other 3 rows
733
+ vW = m_rows[1];
734
+ vX = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(0, 0, 0, 0));
735
+ vY = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(1, 1, 1, 1));
736
+ vZ = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(2, 2, 2, 2));
737
+ vW = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(3, 3, 3, 3));
738
+ vX = _mm_mul_ps(vX, rhs.m_rows[0]);
739
+ vY = _mm_mul_ps(vY, rhs.m_rows[1]);
740
+ vZ = _mm_mul_ps(vZ, rhs.m_rows[2]);
741
+ vW = _mm_mul_ps(vW, rhs.m_rows[3]);
742
+ vX = _mm_add_ps(vX, vZ);
743
+ vY = _mm_add_ps(vY, vW);
744
+ vX = _mm_add_ps(vX, vY);
745
+ m_rows[1] = vX;
746
+
747
+ vW = m_rows[2];
748
+ vX = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(0, 0, 0, 0));
749
+ vY = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(1, 1, 1, 1));
750
+ vZ = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(2, 2, 2, 2));
751
+ vW = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(3, 3, 3, 3));
752
+ vX = _mm_mul_ps(vX, rhs.m_rows[0]);
753
+ vY = _mm_mul_ps(vY, rhs.m_rows[1]);
754
+ vZ = _mm_mul_ps(vZ, rhs.m_rows[2]);
755
+ vW = _mm_mul_ps(vW, rhs.m_rows[3]);
756
+ vX = _mm_add_ps(vX, vZ);
757
+ vY = _mm_add_ps(vY, vW);
758
+ vX = _mm_add_ps(vX, vY);
759
+ m_rows[2] = vX;
760
+
761
+ vW = m_rows[3];
762
+ vX = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(0, 0, 0, 0));
763
+ vY = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(1, 1, 1, 1));
764
+ vZ = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(2, 2, 2, 2));
765
+ vW = _mm_shuffle_ps(vW, vW, _MM_SHUFFLE(3, 3, 3, 3));
766
+ vX = _mm_mul_ps(vX, rhs.m_rows[0]);
767
+ vY = _mm_mul_ps(vY, rhs.m_rows[1]);
768
+ vZ = _mm_mul_ps(vZ, rhs.m_rows[2]);
769
+ vW = _mm_mul_ps(vW, rhs.m_rows[3]);
770
+ vX = _mm_add_ps(vX, vZ);
771
+ vY = _mm_add_ps(vY, vW);
772
+ vX = _mm_add_ps(vX, vY);
773
+ m_rows[3] = vX;
774
+ return *this;
775
+ }
776
+
777
+ inline Matrix Matrix::operator*(const Quaternion& rhs) const
778
+ {
779
+ return operator*(Matrix(rhs));
780
+ }
781
+
782
+ inline Matrix Matrix::operator*=(const Quaternion& rhs)
783
+ {
784
+ return operator*=(Matrix(rhs));
785
+ }
786
+
787
+ inline bool Matrix::operator==(const Matrix& rhs) const
788
+ {
789
+ for (auto i = 0; i < 4; i++)
790
+ {
791
+ for (auto j = 0; j < 4; j++)
792
+ {
793
+ if (m_values[i][j] != rhs.m_values[i][j])
794
+ {
795
+ return false;
796
+ }
797
+ }
798
+ }
799
+
800
+ return true;
801
+ }
802
+ }
MotionCorrection/src/cpp/Math/Quaternion.cpp ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "Quaternion.h"
7
+ #include "Matrix.h"
8
+
9
+ namespace Math
10
+ {
11
+ Quaternion const Quaternion::Identity(0, 0, 0, 1);
12
+
13
+ // Rotation order is XYZ
14
+ EulerAngles Quaternion::ToEulerAngles() const
15
+ {
16
+ return Matrix(*this).ToEulerAngles();
17
+ }
18
+
19
+ Quaternion Quaternion::LookRotation(const Vector& forward, const Vector& up)
20
+ {
21
+ const Vector t = Vector::Cross3(up, forward).Normalize3();
22
+ return Matrix(t, Vector::Cross3(forward, t), forward).GetRotation();
23
+ }
24
+ }
MotionCorrection/src/cpp/Math/Quaternion.h ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Vector.h"
9
+
10
+ namespace Math
11
+ {
12
+ class alignas(16) Quaternion
13
+ {
14
+ public:
15
+
16
+ static Quaternion const Identity;
17
+
18
+ // Calculate the rotation required to align the source vector to the target vector (shortest path)
19
+ static Quaternion FromRotationBetweenNormalizedVectors(const Vector& sourceVector, const Vector& targetVector);
20
+
21
+ // Calculate the rotation required to align one vector onto another but also taking account a fallback rotation axis for opposite parallel vectors
22
+ static Quaternion FromRotationBetweenNormalizedVectors(const Vector& sourceVector, const Vector& targetVector, const Vector& fallbackRotationAxis);
23
+
24
+ // Calculate the rotation required to align the source vector to the target vector (shortest path)
25
+ static Quaternion FromRotationBetweenVectors(const Vector& sourceVector, const Vector& targetVector);
26
+
27
+ // Normalized LERP - not accurate - only use for really short distances
28
+ static Quaternion NLerp(const Quaternion& from, const Quaternion& to, float t);
29
+
30
+ // Standard and accurate Spherical LERP - based on DirectX Math
31
+ static Quaternion SLerp(const Quaternion& from, const Quaternion& to, float t);
32
+
33
+ // Fast approximation of a Spherical LERP - based on "A fast and accurate estimate for SLERP" by David Eberly
34
+ static Quaternion FastSLerp(const Quaternion& from, const Quaternion& to, float t);
35
+
36
+ // Spherical quadrangle/cubic interpolation for quaternions
37
+ static Quaternion SQuad(const Quaternion& q0, const Quaternion& q1, const Quaternion& q2, const Quaternion& q3, float t);
38
+
39
+ // Calculate the shortest delta quaternion needed to rotate 'from' onto 'to'
40
+ static Quaternion Delta(const Quaternion& from, const Quaternion& to);
41
+
42
+ // Simple vector dot product between two quaternions
43
+ static Vector Dot(const Quaternion& q0, const Quaternion& q1);
44
+
45
+ // Calculate the angular distance between two quaternions
46
+ static Radians Distance(const Quaternion& q0, const Quaternion& q1);
47
+
48
+ // Calculate look rotation given forward and up vectors
49
+ static Quaternion LookRotation(const Vector& forward, const Vector& up);
50
+
51
+ public:
52
+
53
+ Quaternion() = default;
54
+ explicit Quaternion(NoInit_t);
55
+ explicit Quaternion(IdentityInit_t);
56
+ explicit Quaternion(const Vector& v);
57
+ explicit Quaternion(float ix, float iy, float iz, float iw);
58
+ explicit Quaternion(const Float4& v);
59
+
60
+ explicit Quaternion(const Vector& axis, Radians angle);
61
+ explicit Quaternion(AxisAngle axisAngle);
62
+
63
+ explicit Quaternion(const EulerAngles& eulerAngles);
64
+ explicit Quaternion(Radians rotX, Radians rotY, Radians rotZ);
65
+
66
+ operator __m128& ();
67
+ operator const __m128& () const;
68
+
69
+ Float4 ToFloat4() const;
70
+ Vector ToVector() const;
71
+
72
+ Vector Length();
73
+ float GetLength() const;
74
+
75
+ // Get the angle this rotation represents around the specified axis
76
+ Radians GetAngle() const;
77
+
78
+ AxisAngle ToAxisAngle() const;
79
+ EulerAngles ToEulerAngles() const;
80
+
81
+ Vector RotateVector(const Vector& vector) const;
82
+ Vector RotateVectorInverse(const Vector& vector) const;
83
+
84
+ Quaternion& Conjugate();
85
+ Quaternion GetConjugate() const;
86
+
87
+ Quaternion& Negate();
88
+ Quaternion GetNegated() const;
89
+
90
+ Quaternion& Invert();
91
+ Quaternion GetInverse() const;
92
+
93
+ Quaternion& Normalize();
94
+ Quaternion GetNormalized() const;
95
+
96
+ Vector XAxis() const noexcept;
97
+ Vector YAxis() const noexcept;
98
+ Vector ZAxis() const noexcept;
99
+
100
+ // Ensure that this rotation is the shortest in terms of the angle (i.e. -5 instead of 355)
101
+ Quaternion& MakeShortestPath();
102
+
103
+ // Ensure that this rotation is the shortest in terms of the angle (i.e. -5 instead of 355)
104
+ Quaternion GetShortestPath() const;
105
+
106
+ // This function will return the estimated normalized quaternion, this is not super accurate but a lot faster (use with care)
107
+ Quaternion& NormalizeInaccurate();
108
+
109
+ // This function will return the estimated normalized quaternion, this is not super accurate but a lot faster (use with care)
110
+ Quaternion GetNormalizedInaccurate() const;
111
+
112
+ bool IsNormalized() const;
113
+ bool IsIdentity() const;
114
+
115
+ // Concatenate the rotation of this onto rhs and return the result i.e. first rotate by rhs then by this
116
+ // This means order of rotation is right-to-left: child-rotation * parent-rotation
117
+ Quaternion operator*(const Quaternion& rhs) const;
118
+ Quaternion& operator*=(const Quaternion& rhs);
119
+
120
+ // Is the distance between this quaternion and another one under the threshold?
121
+ bool IsNearEqual(const Quaternion& rhs, Radians const threshold = Math::DegreesToRadians) const;
122
+
123
+ // Exact equality
124
+ bool operator==(const Quaternion& rhs) const;
125
+
126
+ // Exact equality
127
+ bool operator!=(const Quaternion& rhs) const;
128
+
129
+ private:
130
+
131
+ Vector GetSplatW() const;
132
+ float GetW() const;
133
+
134
+ Quaternion& operator=(const Vector& v) = delete;
135
+
136
+ public:
137
+
138
+ __m128 m_data;
139
+ };
140
+
141
+ static_assert(sizeof(Vector) == 16, "Quaternion size must be 16 bytes!");
142
+ }
143
+
144
+ #include "Quaternion.inl"
MotionCorrection/src/cpp/Math/Quaternion.inl ADDED
@@ -0,0 +1,606 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Quaternion.h"
9
+
10
+ namespace Math
11
+ {
12
+ inline Quaternion Quaternion::FromRotationBetweenNormalizedVectors(const Vector& from, const Vector& to)
13
+ {
14
+ ASSERT(from.IsNormalized3() && to.IsNormalized3());
15
+
16
+ Quaternion result;
17
+
18
+ // Parallel vectors - return zero rotation
19
+ Vector const dot = Vector::Dot3(from, to);
20
+ if (dot.IsGreaterThanEqual4(Vector::OneMinusEpsilon))
21
+ {
22
+ result = Quaternion::Identity;
23
+ }
24
+ // Opposite vectors - return 180 rotation around any orthogonal axis
25
+ else if (dot.IsLessThanEqual4(Vector::EpsilonMinusOne))
26
+ {
27
+ Float4 const fromValues = from.ToFloat4();
28
+ result = Quaternion(-fromValues.m_z, fromValues.m_y, fromValues.m_x, 0);
29
+ result.Normalize();
30
+ }
31
+ else // Calculate quaternion rotation
32
+ {
33
+ Vector const cross = Vector::Cross3(from, to);
34
+ Vector Q = Vector::Select(cross, dot, Vector::Select0001);
35
+ Q += Vector::Select(Vector::Zero, Q.Length4(), Vector::Select0001);
36
+ result = Quaternion(Q);
37
+ result.Normalize();
38
+ }
39
+
40
+ return result;
41
+ }
42
+
43
+ inline Quaternion Quaternion::FromRotationBetweenNormalizedVectors(const Vector& from, const Vector& to, const Vector& fallbackRotationAxis)
44
+ {
45
+ ASSERT(from.IsNormalized3() && to.IsNormalized3());
46
+
47
+ Quaternion Q(NoInit);
48
+
49
+ Vector rotationAxis = from.Cross3(to).GetNormalized3();
50
+ if (rotationAxis.GetLengthSquared3() == 0)
51
+ {
52
+ rotationAxis = fallbackRotationAxis;
53
+ }
54
+
55
+ float const dot = from.GetDot3(to);
56
+ if (dot >= (1.0f - Math::Epsilon))
57
+ {
58
+ Q = Quaternion::Identity;
59
+ }
60
+ else
61
+ {
62
+ float const angle = Math::ACos(dot);
63
+ Q = Quaternion(rotationAxis, angle);
64
+ }
65
+
66
+ return Q;
67
+ }
68
+
69
+ inline Quaternion Quaternion::FromRotationBetweenVectors(const Vector& sourceVector, const Vector& targetVector)
70
+ {
71
+ return FromRotationBetweenNormalizedVectors(
72
+ sourceVector.GetNormalized3(),
73
+ targetVector.GetNormalized3());
74
+ }
75
+
76
+ inline Quaternion Quaternion::NLerp(const Quaternion& from, const Quaternion& to, float T)
77
+ {
78
+ ASSERT(T >= 0.0f && T <= 1.0f);
79
+
80
+ Quaternion adjustedFrom(from);
81
+
82
+ // Ensure that the rotations are in the same direction
83
+ if (Quaternion::Dot(from, to).IsLessThan4(Vector::Zero))
84
+ {
85
+ adjustedFrom.Negate();
86
+ }
87
+
88
+ Quaternion result(Vector::Lerp(adjustedFrom.ToVector(), to.ToVector(), T));
89
+ result.Normalize();
90
+ return result;
91
+ }
92
+
93
+ inline Quaternion Quaternion::SLerp(const Quaternion& from, const Quaternion& to, float T)
94
+ {
95
+ ASSERT(T >= 0.0f && T <= 1.0f);
96
+
97
+ static SIMD::UIntMask const maskSign = { 0x80000000,0x00000000,0x00000000,0x00000000 };
98
+ static __m128 const oneMinusEpsilon = { 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f, 1.0f - 0.00001f };
99
+
100
+ Vector const VecT(T);
101
+
102
+ Vector cosOmega = Quaternion::Dot(from, to);
103
+
104
+ Vector control = cosOmega.LessThan(Vector::Zero);
105
+ Vector sign = Vector::Select(Vector::One, Vector::NegativeOne, control);
106
+
107
+ cosOmega = _mm_mul_ps(cosOmega, sign);
108
+ control = cosOmega.LessThan(oneMinusEpsilon);
109
+
110
+ Vector sinOmega = _mm_mul_ps(cosOmega, cosOmega);
111
+ sinOmega = _mm_sub_ps(Vector::One, sinOmega);
112
+ sinOmega = _mm_sqrt_ps(sinOmega);
113
+
114
+ Vector omega = Vector::ATan2(sinOmega, cosOmega);
115
+
116
+ Vector V01 = _mm_shuffle_ps(VecT, VecT, _MM_SHUFFLE(2, 3, 0, 1));
117
+ V01 = _mm_and_ps(V01, SIMD::g_maskXY00);
118
+ V01 = _mm_xor_ps(V01, maskSign);
119
+ V01 = _mm_add_ps(Vector::UnitX, V01);
120
+
121
+ Vector S0 = _mm_mul_ps(V01, omega);
122
+ S0 = Vector::Sin(S0);
123
+ S0 = _mm_div_ps(S0, sinOmega);
124
+ S0 = Vector::Select(V01, S0, control);
125
+
126
+ Vector S1 = S0.GetSplatY();
127
+ S0 = S0.GetSplatX();
128
+
129
+ S1 = _mm_mul_ps(S1, sign);
130
+ Vector result = _mm_mul_ps(from, S0);
131
+ S1 = _mm_mul_ps(S1, to);
132
+ result = _mm_add_ps(result, S1);
133
+
134
+ return Quaternion(result);
135
+ }
136
+
137
+ inline Quaternion Quaternion::FastSLerp(const Quaternion& q0, const Quaternion& q1, float t)
138
+ {
139
+ // Precomputed constants
140
+ constexpr float const mu = 1.85298109240830f;
141
+ static Vector const u0123 = _mm_setr_ps(1.f / (1 * 3), 1.f / (2 * 5), 1.f / (3 * 7), 1.f / (4 * 9));
142
+ static Vector const u4567 = _mm_setr_ps(1.f / (5 * 11), 1.f / (6 * 13), 1.f / (7 * 15), mu / (8 * 17));
143
+ static Vector const v0123 = _mm_setr_ps(1.f / 3, 2.f / 5, 3.f / 7, 4.f / 9);
144
+ static Vector const v4567 = _mm_setr_ps(5.f / 11, 6.f / 13, 7.f / 15, mu * 8 / 17);
145
+ static Vector const vSignMask = _mm_set1_ps(-0.f);
146
+
147
+ // Common code for computing the scalar coefficients of SLERP
148
+ auto CalculateCoefficient = [](Vector vT, Vector xm1)
149
+ {
150
+ Vector const vTSquared = vT * vT;
151
+
152
+ // ( b4, b5, b6, b7 ) = ( x-1 ) * ( u4 * t^2 - v4, u5 * t^2 - v5, u6 * t^2 - v6, u7 * t^2 - v7 )
153
+ Vector b4567 = Vector::MultiplySubtract(u4567, vTSquared, v4567);
154
+ b4567 *= xm1;
155
+
156
+ // ( b7, b7, b7, b7 )
157
+ Vector b = b4567.GetSplatW();
158
+ Vector c = b + Vector::One;
159
+
160
+ // ( b6, b6, b6, b6 )
161
+ b = b4567.GetSplatZ();
162
+ c = Vector::MultiplyAdd(b, c, Vector::One);
163
+
164
+ // ( b5, b5, b5, b5 )
165
+ b = b4567.GetSplatY();
166
+ c = Vector::MultiplyAdd(b, c, Vector::One);
167
+
168
+ // ( b4, b4, b4, b4 )
169
+ b = b4567.GetSplatX();
170
+ c = Vector::MultiplyAdd(b, c, Vector::One);
171
+
172
+ // ( b0, b1, b2, b3 ) =
173
+ // ( x-1)*(u0* t^2-v0, u1 * t^2 -v1, u2* t^2-v2, u3* t^2-v3 )
174
+ Vector b0123 = Vector::MultiplySubtract(u0123, vTSquared, v0123);
175
+ b0123 *= xm1;
176
+
177
+ // ( b3, b3, b3, b3 )
178
+ b = b0123.GetSplatW();
179
+ c = Vector::MultiplyAdd(b, c, Vector::One);
180
+
181
+ // ( b2, b2, b2, b2 )
182
+ b = b0123.GetSplatZ();
183
+ c = Vector::MultiplyAdd(b, c, Vector::One);
184
+
185
+ // ( b1, b1, b1, b1 )
186
+ b = b0123.GetSplatY();
187
+ c = Vector::MultiplyAdd(b, c, Vector::One);
188
+
189
+ // ( b0, b0, b0, b0 )
190
+ b = b0123.GetSplatX();
191
+ c = Vector::MultiplyAdd(b, c, Vector::One);
192
+ c *= vT;
193
+
194
+ return c;
195
+ };
196
+
197
+ Vector x = Vector::Dot4(q0.m_data, q1.m_data); // cos ( theta ) in all components
198
+
199
+ Vector sign = _mm_and_ps(vSignMask, x);
200
+ x = _mm_xor_ps(sign, x);
201
+ Vector localQ1 = _mm_xor_ps(sign, q1);
202
+
203
+ Vector xm1 = x - Vector::One;
204
+
205
+ Vector cT = CalculateCoefficient(Vector(t), xm1);
206
+ Vector cD = CalculateCoefficient(Vector(1.0f - t), xm1);
207
+ cT = cT * localQ1;
208
+
209
+ Quaternion result(Vector::MultiplyAdd(cD, q0.m_data, cT));
210
+ return result;
211
+ }
212
+
213
+ inline Quaternion Quaternion::SQuad(const Quaternion& q0, const Quaternion& q1, const Quaternion& q2, const Quaternion& q3, float t)
214
+ {
215
+ ASSERT(t >= 0.0f && t <= 1.0f);
216
+
217
+ Quaternion const q03 = Quaternion::SLerp(q0, q3, t);
218
+ Quaternion const q12 = Quaternion::SLerp(q1, q2, t);
219
+ t = (t - (t * t)) * 2;
220
+ Quaternion const result = Quaternion::SLerp(q03, q12, t);
221
+ return result;
222
+ }
223
+
224
+ inline Quaternion Quaternion::Delta(const Quaternion& from, const Quaternion& to)
225
+ {
226
+ return to * from.GetInverse();
227
+ }
228
+
229
+ inline Vector Quaternion::Dot(const Quaternion& q0, const Quaternion& q1)
230
+ {
231
+ return Vector::Dot4(q0.m_data, q1.m_data);
232
+ }
233
+
234
+ inline Radians Quaternion::Distance(const Quaternion& q0, const Quaternion& q1)
235
+ {
236
+ float const dot = Math::Clamp(Dot(q0, q1).ToFloat(), -1.0f, 1.0f);
237
+ return Radians(2 * Math::ACos(Math::Abs(dot)));
238
+ }
239
+
240
+ inline Quaternion::Quaternion(NoInit_t)
241
+ {
242
+ }
243
+
244
+ inline Quaternion::Quaternion(IdentityInit_t)
245
+ : m_data(Vector::UnitW.m_data)
246
+ {
247
+ }
248
+
249
+ inline Quaternion::Quaternion(const Vector& v)
250
+ : m_data(v.m_data)
251
+ {
252
+ }
253
+
254
+ inline Quaternion::Quaternion(float ix, float iy, float iz, float iw)
255
+ {
256
+ m_data = _mm_set_ps(iw, iz, iy, ix);
257
+ }
258
+
259
+ inline Quaternion::Quaternion(const Float4& v)
260
+ : Quaternion(v.m_x, v.m_y, v.m_z, v.m_w)
261
+ {
262
+ }
263
+
264
+ inline Quaternion::Quaternion(const Vector& axis, Radians angle)
265
+ {
266
+ ASSERT(axis.IsNormalized3());
267
+
268
+ auto N = _mm_and_ps(axis, SIMD::g_maskXYZ0);
269
+ N = _mm_or_ps(N, Vector::UnitW);
270
+ auto scale = _mm_set_ps1(0.5f * (float)angle);
271
+
272
+ Vector sine, cosine;
273
+ Vector::SinCos(sine, cosine, scale);
274
+
275
+ scale = _mm_and_ps(sine, SIMD::g_maskXYZ0);
276
+ cosine = _mm_and_ps(cosine, SIMD::g_mask000W);
277
+ scale = _mm_or_ps(scale, cosine);
278
+
279
+ N = _mm_mul_ps(N, scale);
280
+ m_data = N;
281
+ }
282
+
283
+ inline Quaternion::Quaternion(AxisAngle axisAngle)
284
+ : Quaternion(Vector(axisAngle.m_axis), axisAngle.m_angle)
285
+ {
286
+ }
287
+
288
+ inline Quaternion::Quaternion(const EulerAngles& eulerAngles)
289
+ {
290
+ auto const rotationX = Quaternion(Vector::UnitX, eulerAngles.m_x);
291
+ auto const rotationY = Quaternion(Vector::UnitY, eulerAngles.m_y);
292
+ auto const rotationZ = Quaternion(Vector::UnitZ, eulerAngles.m_z);
293
+
294
+ // Rotation order is XYZ - all in global space, hence the order is reversed
295
+ m_data = (rotationX * rotationY * rotationZ).GetNormalized().m_data;
296
+ }
297
+
298
+ inline Quaternion::Quaternion(Radians rotX, Radians rotY, Radians rotZ)
299
+ : Quaternion(EulerAngles(rotX, rotY, rotZ))
300
+ {
301
+ }
302
+
303
+ inline Quaternion::operator __m128& ()
304
+ {
305
+ return m_data;
306
+ }
307
+
308
+ inline Quaternion::operator const __m128& () const
309
+ {
310
+ return m_data;
311
+ }
312
+
313
+ inline Float4 Quaternion::ToFloat4() const
314
+ {
315
+ Float4 v;
316
+ _mm_storeu_ps(&v.m_x, m_data);
317
+ return v;
318
+ }
319
+
320
+ inline Vector Quaternion::ToVector() const
321
+ {
322
+ return Vector(m_data);
323
+ }
324
+
325
+ inline Vector Quaternion::Length()
326
+ {
327
+ return ToVector().Length4();
328
+ }
329
+
330
+ inline float Quaternion::GetLength() const
331
+ {
332
+ return ToVector().GetLength4();
333
+ }
334
+
335
+ inline Radians Quaternion::GetAngle() const
336
+ {
337
+ return Radians(2.0f * Math::ACos(GetW()));
338
+ }
339
+
340
+ inline AxisAngle Quaternion::ToAxisAngle() const
341
+ {
342
+ return AxisAngle(ToVector(), Radians(2.0f * Math::ACos(GetW())));
343
+ }
344
+
345
+ inline Vector Quaternion::RotateVector(const Vector& vector) const
346
+ {
347
+ Quaternion const A(Vector::Select(Vector::Select1110, vector, Vector::Select1110));
348
+ Quaternion const result = GetConjugate() * A;
349
+ return (result * *this).ToVector();
350
+ }
351
+
352
+ inline Vector Quaternion::RotateVectorInverse(const Vector& vector) const
353
+ {
354
+ Quaternion const A(Vector::Select(Vector::Select1110, vector, Vector::Select1110));
355
+ Quaternion const result = *this * A;
356
+ return (result * GetConjugate()).ToVector();
357
+ }
358
+
359
+ inline Quaternion& Quaternion::Conjugate()
360
+ {
361
+ static __m128 const conj = { -1.0f, -1.0f, -1.0f, 1.0f };
362
+ m_data = _mm_mul_ps(*this, conj);
363
+ return *this;
364
+ }
365
+
366
+ inline Quaternion Quaternion::GetConjugate() const
367
+ {
368
+ Quaternion q = *this;
369
+ q.Conjugate();
370
+ return q;
371
+ }
372
+ inline Quaternion& Quaternion::Negate()
373
+ {
374
+ m_data = _mm_mul_ps(*this, Vector::NegativeOne);
375
+ return *this;
376
+ }
377
+
378
+ inline Quaternion Quaternion::GetNegated() const
379
+ {
380
+ Quaternion q = *this;
381
+ q.Negate();
382
+ return q;
383
+ }
384
+
385
+ inline Quaternion& Quaternion::Invert()
386
+ {
387
+ Vector const conjugate(GetConjugate().m_data);
388
+ Vector const length = ToVector().Length4();
389
+ Vector const mask = length.LessThanEqual(Vector::Epsilon);
390
+ Vector const result = conjugate / length;
391
+ m_data = result.Select(result, Vector::Zero, mask);
392
+ return *this;
393
+ }
394
+
395
+ inline Quaternion Quaternion::GetInverse() const
396
+ {
397
+ Quaternion q = *this;
398
+ q.Invert();
399
+ return q;
400
+ }
401
+
402
+ inline Quaternion& Quaternion::Normalize()
403
+ {
404
+ m_data = ToVector().GetNormalized4().m_data;
405
+ return *this;
406
+ }
407
+
408
+ inline Quaternion Quaternion::GetNormalized() const
409
+ {
410
+ Quaternion q = *this;
411
+ q.Normalize();
412
+ return q;
413
+ }
414
+
415
+ inline Vector Quaternion::XAxis() const noexcept
416
+ {
417
+ const float x = _mm_cvtss_f32(m_data);
418
+ const float y = _mm_cvtss_f32(
419
+ _mm_shuffle_ps(m_data, m_data,
420
+ _MM_SHUFFLE(1, 1, 1, 1)));
421
+ const float z = _mm_cvtss_f32(
422
+ _mm_shuffle_ps(m_data, m_data,
423
+ _MM_SHUFFLE(2, 2, 2, 2)));
424
+ const float w = _mm_cvtss_f32(
425
+ _mm_shuffle_ps(m_data, m_data,
426
+ _MM_SHUFFLE(3, 3, 3, 3)));
427
+
428
+ const float s = 2.0f * w;
429
+ const float x2 = 2.0f * x;
430
+
431
+ return Vector(
432
+ x2 * x + s * w - 1.0f,
433
+ x2 * y + s * z,
434
+ x2 * z + s * -y);
435
+ }
436
+
437
+ inline Vector Quaternion::YAxis() const noexcept
438
+ {
439
+ const float x = _mm_cvtss_f32(m_data);
440
+ const float y = _mm_cvtss_f32(
441
+ _mm_shuffle_ps(m_data, m_data,
442
+ _MM_SHUFFLE(1, 1, 1, 1)));
443
+ const float z = _mm_cvtss_f32(
444
+ _mm_shuffle_ps(m_data, m_data,
445
+ _MM_SHUFFLE(2, 2, 2, 2)));
446
+ const float w = _mm_cvtss_f32(
447
+ _mm_shuffle_ps(m_data, m_data,
448
+ _MM_SHUFFLE(3, 3, 3, 3)));
449
+
450
+ const float s = 2.0f * w;
451
+ const float y2 = 2.0f * y;
452
+
453
+ return Vector(
454
+ y2 * x + s * -z,
455
+ y2 * y + s * w - 1.0f,
456
+ y2 * z + s * x);
457
+ }
458
+
459
+ inline Vector Quaternion::ZAxis() const noexcept
460
+ {
461
+ const float x = _mm_cvtss_f32(m_data);
462
+ const float y = _mm_cvtss_f32(
463
+ _mm_shuffle_ps(m_data, m_data,
464
+ _MM_SHUFFLE(1, 1, 1, 1)));
465
+ const float z = _mm_cvtss_f32(
466
+ _mm_shuffle_ps(m_data, m_data,
467
+ _MM_SHUFFLE(2, 2, 2, 2)));
468
+ const float w = _mm_cvtss_f32(
469
+ _mm_shuffle_ps(m_data, m_data,
470
+ _MM_SHUFFLE(3, 3, 3, 3)));
471
+
472
+ const float s = 2.0f * w;
473
+ const float z2 = 2.0f * z;
474
+
475
+ return Vector(
476
+ x * z2 + s * y,
477
+ y * z2 + s * -x,
478
+ z * z2 + s * w - 1.0f);
479
+ }
480
+
481
+ inline Quaternion& Quaternion::MakeShortestPath()
482
+ {
483
+ // If we have a > 180 angle, negate
484
+ // w < 0.0f is the same as dot( identity, q ) < 0
485
+ if (GetW() < 0.0f)
486
+ {
487
+ Negate();
488
+ }
489
+
490
+ return *this;
491
+ }
492
+
493
+ inline Quaternion Quaternion::GetShortestPath() const
494
+ {
495
+ Quaternion sp = *this;
496
+ sp.MakeShortestPath();
497
+ return sp;
498
+ }
499
+
500
+ inline Quaternion& Quaternion::NormalizeInaccurate()
501
+ {
502
+ *this = GetNormalizedInaccurate();
503
+ return *this;
504
+ }
505
+
506
+ inline Quaternion Quaternion::GetNormalizedInaccurate() const
507
+ {
508
+ __m128 vLengthSq = _mm_mul_ps(m_data, m_data);
509
+ __m128 vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(3, 2, 3, 2));
510
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
511
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 0, 0, 0));
512
+ vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0));
513
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
514
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 2, 2, 2));
515
+
516
+ // Get the reciprocal and mul to perform the normalization
517
+ Quaternion result;
518
+ result.m_data = _mm_rsqrt_ps(vLengthSq);
519
+ result.m_data = _mm_mul_ps(result.m_data, m_data);
520
+ return result;
521
+ }
522
+
523
+ inline bool Quaternion::IsNormalized() const
524
+ {
525
+ return ToVector().IsNormalized4();
526
+ }
527
+
528
+ inline bool Quaternion::IsIdentity() const
529
+ {
530
+ return ToVector().IsEqual3(Vector::UnitW);
531
+ }
532
+
533
+ inline Quaternion Quaternion::operator*(const Quaternion& rhs) const
534
+ {
535
+ static const __m128 controlWZYX = { 1.0f,-1.0f, 1.0f,-1.0f };
536
+ static const __m128 controlZWXY = { 1.0f, 1.0f,-1.0f,-1.0f };
537
+ static const __m128 controlYXWZ = { -1.0f, 1.0f, 1.0f,-1.0f };
538
+
539
+ // Copy to SSE registers and use as few as possible for x86
540
+ __m128 Q2X = rhs;
541
+ __m128 Q2Y = rhs;
542
+ __m128 Q2Z = rhs;
543
+ __m128 vResult = rhs;
544
+ // Splat with one instruction
545
+ vResult = _mm_shuffle_ps(vResult, vResult, _MM_SHUFFLE(3, 3, 3, 3));
546
+ Q2X = _mm_shuffle_ps(Q2X, Q2X, _MM_SHUFFLE(0, 0, 0, 0));
547
+ Q2Y = _mm_shuffle_ps(Q2Y, Q2Y, _MM_SHUFFLE(1, 1, 1, 1));
548
+ Q2Z = _mm_shuffle_ps(Q2Z, Q2Z, _MM_SHUFFLE(2, 2, 2, 2));
549
+ // Retire Q1 and perform Q1*Q2W
550
+ vResult = _mm_mul_ps(vResult, *this);
551
+ __m128 Q1Shuffle = *this;
552
+ // Shuffle the copies of Q1
553
+ Q1Shuffle = _mm_shuffle_ps(Q1Shuffle, Q1Shuffle, _MM_SHUFFLE(0, 1, 2, 3));
554
+ // Mul by Q1WZYX
555
+ Q2X = _mm_mul_ps(Q2X, Q1Shuffle);
556
+ Q1Shuffle = _mm_shuffle_ps(Q1Shuffle, Q1Shuffle, _MM_SHUFFLE(2, 3, 0, 1));
557
+ // Flip the signs on m_y and m_z
558
+ Q2X = _mm_mul_ps(Q2X, controlWZYX);
559
+ // Mul by Q1ZWXY
560
+ Q2Y = _mm_mul_ps(Q2Y, Q1Shuffle);
561
+ Q1Shuffle = _mm_shuffle_ps(Q1Shuffle, Q1Shuffle, _MM_SHUFFLE(0, 1, 2, 3));
562
+ // Flip the signs on m_z and m_w
563
+ Q2Y = _mm_mul_ps(Q2Y, controlZWXY);
564
+ // Mul by Q1YXWZ
565
+ Q2Z = _mm_mul_ps(Q2Z, Q1Shuffle);
566
+ vResult = _mm_add_ps(vResult, Q2X);
567
+ // Flip the signs on m_x and m_w
568
+ Q2Z = _mm_mul_ps(Q2Z, controlYXWZ);
569
+ Q2Y = _mm_add_ps(Q2Y, Q2Z);
570
+ vResult = _mm_add_ps(vResult, Q2Y);
571
+
572
+ return Quaternion(vResult);
573
+ }
574
+
575
+ inline Quaternion& Quaternion::operator*=(const Quaternion& rhs)
576
+ {
577
+ *this = *this * rhs;
578
+ return *this;
579
+ }
580
+
581
+ inline bool Quaternion::IsNearEqual(const Quaternion& rhs, Radians const threshold) const
582
+ {
583
+ return Quaternion::Distance(*this, rhs) <= threshold;
584
+ }
585
+
586
+ inline bool Quaternion::operator==(const Quaternion& rhs) const
587
+ {
588
+ return ToVector() == rhs.ToVector();
589
+ }
590
+
591
+ inline bool Quaternion::operator!=(const Quaternion& rhs) const
592
+ {
593
+ return !operator==(rhs);
594
+ }
595
+
596
+ inline Vector Quaternion::GetSplatW() const
597
+ {
598
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(3, 3, 3, 3));
599
+ }
600
+
601
+ inline float Quaternion::GetW() const
602
+ {
603
+ auto vTemp = GetSplatW();
604
+ return _mm_cvtss_f32(vTemp);
605
+ }
606
+ }
MotionCorrection/src/cpp/Math/SIMD.h ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include <stdint.h>
9
+ #include <immintrin.h>
10
+
11
+ namespace SIMD
12
+ {
13
+ struct alignas( 16 ) IntMask
14
+ {
15
+ inline operator __m128( ) const { return reinterpret_cast<__m128 const&>( *this ); }
16
+ inline operator __m128i( ) const { return _mm_castps_si128( *this ); }
17
+ inline operator __m128d( ) const { return _mm_castps_pd( *this ); }
18
+
19
+ int32_t i[4];
20
+ };
21
+
22
+ struct alignas( 16 ) UIntMask
23
+ {
24
+ inline operator __m128( ) const { return reinterpret_cast<__m128 const&>( *this ); }
25
+ inline operator __m128i( ) const { return _mm_castps_si128( *this ); }
26
+ inline operator __m128d( ) const { return _mm_castps_pd( *this ); }
27
+
28
+ uint32_t v[4];
29
+ };
30
+
31
+ struct alignas( 16 ) FloatMask
32
+ {
33
+ inline operator __m128() const { return reinterpret_cast<__m128 const&>( *this ); }
34
+ inline operator __m128i() const { return _mm_castps_si128( *this ); }
35
+ inline operator __m128d() const { return _mm_castps_pd( *this ); }
36
+
37
+ float v[4];
38
+ };
39
+
40
+ // Int Operations
41
+ //-------------------------------------------------------------------------
42
+
43
+ namespace Int
44
+ {
45
+ FORCE_INLINE bool Equal( __m128 V1, __m128 V2 )
46
+ {
47
+ __m128i vTemp = _mm_cmpeq_epi32( _mm_castps_si128( V1 ), _mm_castps_si128( V2 ) );
48
+ return ( ( ( _mm_movemask_ps( _mm_castsi128_ps( vTemp ) ) & 7 ) == 7 ) != 0 );
49
+ }
50
+
51
+ FORCE_INLINE bool NotEqual( __m128 V1, __m128 V2 )
52
+ {
53
+ __m128i vTemp = _mm_cmpeq_epi32( _mm_castps_si128( V1 ), _mm_castps_si128( V2 ) );
54
+ return ( ( _mm_movemask_ps( _mm_castsi128_ps( vTemp ) ) != 0xF ) != 0 );
55
+ }
56
+
57
+ FORCE_INLINE __m128 And( __m128 V1, __m128 V2 )
58
+ {
59
+ return _mm_and_ps( V1, V2 );
60
+ }
61
+
62
+ FORCE_INLINE __m128 Or( __m128 V1, __m128 V2 )
63
+ {
64
+ __m128i V = _mm_or_si128( _mm_castps_si128( V1 ), _mm_castps_si128( V2 ) );
65
+ return _mm_castsi128_ps( V );
66
+ }
67
+ }
68
+
69
+ //-------------------------------------------------------------------------
70
+
71
+ static __m128 const g_sinCoefficients0 = { -0.16666667f, +0.0083333310f, -0.00019840874f, +2.7525562e-06f };
72
+ static __m128 const g_sinCoefficients1 = { -2.3889859e-08f, -0.16665852f, +0.0083139502f, -0.00018524670f };
73
+ static __m128 const g_cosCoefficients0 = { -0.5f, +0.041666638f, -0.0013888378f, +2.4760495e-05f };
74
+ static __m128 const g_cosCoefficients1 = { -2.6051615e-07f, -0.49992746f, +0.041493919f, -0.0012712436f };
75
+ static __m128 const g_tanCoefficients0 = { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f };
76
+ static __m128 const g_tanCoefficients1 = { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f };
77
+ static __m128 const g_tanCoefficients2 = { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f };
78
+ static __m128 const g_arcCoefficients0 = { +1.5707963050f, -0.2145988016f, +0.0889789874f, -0.0501743046f };
79
+ static __m128 const g_arcCoefficients1 = { +0.0308918810f, -0.0170881256f, +0.0066700901f, -0.0012624911f };
80
+ static __m128 const g_aTanCoefficients0 = { -0.3333314528f, +0.1999355085f, -0.1420889944f, +0.1065626393f };
81
+ static __m128 const g_aTanCoefficients1 = { -0.0752896400f, +0.0429096138f, -0.0161657367f, +0.0028662257f };
82
+ static __m128 const g_aTanEstCoefficients0 = { +0.999866f, +0.999866f, +0.999866f, +0.999866f };
83
+ static __m128 const g_aTanEstCoefficients1 = { -0.3302995f, +0.180141f, -0.085133f, +0.0208351f };
84
+ static __m128 const g_tanEstCoefficients = { 2.484f, -1.954923183e-1f, 2.467401101f, Math::OneDivPi };
85
+ static __m128 const g_arcEstCoefficients = { +1.5707288f,-0.2121144f,+0.0742610f,-0.0187293f };
86
+ static __m128 const g_aTan2Constants = { Math::Pi, Math::PiDivTwo, Math::PiDivFour, 2.3561944905f /* 3/4 Pi */ };
87
+
88
+ //-------------------------------------------------------------------------
89
+
90
+ static FloatMask const g_noFraction = { 8388608.0f,8388608.0f,8388608.0f,8388608.0f };
91
+ static IntMask const g_absMask = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
92
+ static UIntMask const g_trueMask = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
93
+ static UIntMask const g_signMask = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
94
+ static UIntMask const g_maskX000 = { 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000 };
95
+ static UIntMask const g_mask0Y00 = { 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000 };
96
+ static UIntMask const g_mask00Z0 = { 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000 };
97
+ static UIntMask const g_mask000W = { 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF };
98
+ static UIntMask const g_maskXY00 = { 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000 };
99
+ static UIntMask const g_maskXYZ0 = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 };
100
+ }
MotionCorrection/src/cpp/Math/Scalar.h ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Compiler.h"
9
+ #include "Debug.h"
10
+
11
+ #include "Constants.h"
12
+
13
+ #include <math.h>
14
+ #include <stdint.h>
15
+
16
+ //
17
+ // Scalar related methods
18
+ //
19
+
20
+ namespace Math
21
+ {
22
+ FORCE_INLINE float Sin( float value ) { return sinf( value ); }
23
+ FORCE_INLINE float Cos( float value ) { return cosf( value ); }
24
+ FORCE_INLINE float Tan( float value ) { return tanf( value ); }
25
+
26
+ FORCE_INLINE float ASin( float value ) { return asinf( value ); }
27
+ FORCE_INLINE float ACos( float value ) { return acosf( value ); }
28
+ FORCE_INLINE float ATan( float value ) { return atanf( value ); }
29
+ FORCE_INLINE float ATan2( float y, float x ) { return atan2f( y, x ); }
30
+
31
+ FORCE_INLINE float Cosec( float value ) { return 1.0f / sinf( value ); }
32
+ FORCE_INLINE float Sec( float value ) { return 1.0f / cosf( value ); }
33
+ FORCE_INLINE float Cot( float value ) { return 1.0f / tanf( PiDivTwo - value ); }
34
+
35
+ FORCE_INLINE float Pow( float x, float y ) { return powf( x, y ); }
36
+ FORCE_INLINE float Sqr( float value ) { return value * value; }
37
+ FORCE_INLINE float Sqrt( float value ) { return sqrtf( value ); }
38
+
39
+ FORCE_INLINE float Log( float value ) { return logf( value ); }
40
+ FORCE_INLINE float Log2f( float value ) { return log2f( value ); }
41
+
42
+ FORCE_INLINE float AddToMovingAverage( float currentAverage, uint64_t numCurrentSamples, float newValue )
43
+ {
44
+ return currentAverage + ( ( newValue - currentAverage ) / float( numCurrentSamples + 1 ) );
45
+ }
46
+
47
+ FORCE_INLINE float Abs( float a ) { return fabsf( a ); }
48
+ FORCE_INLINE double Abs( double a ) { return fabs( a ); }
49
+ FORCE_INLINE int8_t Abs( int8_t a ) { return (int8_t) abs( a ); }
50
+ FORCE_INLINE int16_t Abs( int16_t a ) { return (int16_t) abs( a ); }
51
+ FORCE_INLINE int32_t Abs( int32_t a ) { return labs( a ); }
52
+ FORCE_INLINE int64_t Abs( int64_t a ) { return llabs( a ); }
53
+
54
+ FORCE_INLINE float Reciprocal( float r ) { return 1.0f / r; }
55
+ FORCE_INLINE double Reciprocal( double r ) { return 1.0 / r; }
56
+
57
+ template<typename T>
58
+ FORCE_INLINE T Min( T a, T b ) { return a <= b ? a : b; }
59
+
60
+ template<typename T>
61
+ FORCE_INLINE T Max( T a, T b ) { return a >= b ? a : b; }
62
+
63
+ template<typename T>
64
+ FORCE_INLINE T AbsMin( T a, T b ) { return Abs( a ) <= Abs( b ) ? a : b; }
65
+
66
+ template<typename T>
67
+ FORCE_INLINE T AbsMax( T a, T b ) { return Abs( a ) >= Abs( b ) ? a : b; }
68
+
69
+ template<typename T>
70
+ FORCE_INLINE T Sqrt( T a ) { return sqrt( a ); }
71
+
72
+ template<typename T>
73
+ FORCE_INLINE T Clamp( T value, T lowerBound, T upperBound )
74
+ {
75
+ ASSERT( lowerBound <= upperBound );
76
+ return Min( Max( value, lowerBound ), upperBound );
77
+ }
78
+
79
+ template<typename T>
80
+ FORCE_INLINE bool IsInRangeInclusive( T value, T lowerBound, T upperBound )
81
+ {
82
+ ASSERT( lowerBound < upperBound );
83
+ return value >= lowerBound && value <= upperBound;
84
+ }
85
+
86
+ template<typename T>
87
+ FORCE_INLINE bool IsInRangeExclusive( T value, T lowerBound, T upperBound )
88
+ {
89
+ ASSERT( lowerBound < upperBound );
90
+ return value > lowerBound && value < upperBound;
91
+ }
92
+
93
+ // Decomposes a float into integer and remainder portions, remainder is return and the integer result is stored in the integer portion
94
+ FORCE_INLINE float ModF( float value, float& integerPortion )
95
+ {
96
+ return modff( value, &integerPortion );
97
+ }
98
+
99
+ // Returns the floating point remainder of x/y
100
+ FORCE_INLINE float FModF( float x, float y )
101
+ {
102
+ return fmodf( x, y );
103
+ }
104
+
105
+ template<typename T>
106
+ FORCE_INLINE T Lerp( T A, T B, float t )
107
+ {
108
+ return A + ( B - A ) * t;
109
+ }
110
+
111
+ FORCE_INLINE float PercentageThroughRange( float value, float lowerBound, float upperBound )
112
+ {
113
+ ASSERT( lowerBound < upperBound );
114
+ return Clamp( value, lowerBound, upperBound ) / ( upperBound - lowerBound );
115
+ }
116
+
117
+ FORCE_INLINE bool IsNearEqual( float value, float comparand, float epsilon = Epsilon )
118
+ {
119
+ return fabsf( value - comparand ) <= epsilon;
120
+ }
121
+
122
+ FORCE_INLINE bool IsNearZero( float value, float epsilon = Epsilon )
123
+ {
124
+ return fabsf( value ) <= epsilon;
125
+ }
126
+
127
+ FORCE_INLINE bool IsNearEqual( double value, double comparand, double epsilon = Epsilon )
128
+ {
129
+ return fabs( value - comparand ) <= epsilon;
130
+ }
131
+
132
+ FORCE_INLINE bool IsNearZero( double value, double epsilon = Epsilon )
133
+ {
134
+ return fabs( value ) <= epsilon;
135
+ }
136
+
137
+ FORCE_INLINE float Ceiling( float value )
138
+ {
139
+ return ceilf( value );
140
+ }
141
+
142
+ FORCE_INLINE int32_t CeilingToInt( float value )
143
+ {
144
+ return (int32_t) ceilf( value );
145
+ }
146
+
147
+ FORCE_INLINE float Floor( float value )
148
+ {
149
+ return floorf( value );
150
+ }
151
+
152
+ FORCE_INLINE int32_t FloorToInt( float value )
153
+ {
154
+ return (int32_t) floorf( value );
155
+ }
156
+
157
+ FORCE_INLINE float Round( float value )
158
+ {
159
+ return roundf( value );
160
+ }
161
+
162
+ FORCE_INLINE int32_t RoundToInt( float value )
163
+ {
164
+ return (int32_t) roundf( value );
165
+ }
166
+
167
+ inline float RemapRange( float value, float fromRangeBegin, float fromRangeEnd, float toRangeBegin, float toRangeEnd )
168
+ {
169
+ float const fromRangeLength = fromRangeEnd - fromRangeBegin;
170
+ float const percentageThroughFromRange = Clamp( ( value - fromRangeBegin ) / fromRangeLength, 0.0f, 1.0f );
171
+ float const toRangeLength = toRangeEnd - toRangeBegin;
172
+ float const result = toRangeBegin + ( percentageThroughFromRange * toRangeLength );
173
+
174
+ return result;
175
+ }
176
+
177
+ FORCE_INLINE float Square( float value )
178
+ {
179
+ return value * value;
180
+ }
181
+
182
+ FORCE_INLINE float SmoothStep01( float value )
183
+ {
184
+ value = Clamp( value, 0.0f, 1.0f );
185
+ return value * value * ( 3.0f - 2.0f * value );
186
+ }
187
+ }
MotionCorrection/src/cpp/Math/Transform.cpp ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "Transform.h"
7
+
8
+ namespace Math
9
+ {
10
+ Transform const Transform::Identity = Transform(Quaternion(0, 0, 0, 1), Vector(0, 0, 0, 1), 1.0f);
11
+
12
+ void Transform::SanitizeScaleValue()
13
+ {
14
+ if (Math::IsNearEqual(GetScale(), 1.0f, Math::LargeEpsilon))
15
+ {
16
+ SetScale(1.0f);
17
+ }
18
+ }
19
+ }
MotionCorrection/src/cpp/Math/Transform.h ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Matrix.h"
9
+
10
+ namespace Math
11
+ {
12
+ //
13
+ // VQS Transform
14
+ //
15
+
16
+ class Transform
17
+ {
18
+ public:
19
+
20
+ static Transform const Identity;
21
+
22
+ static Transform FromRotation(const Quaternion& rotation);
23
+ static Transform FromTranslation(const Vector& translation);
24
+ static Transform FromScale(float uniformScale);
25
+ static Transform FromTranslationAndScale(const Vector& translation, float uniformScale);
26
+ static Transform FromRotationBetweenVectors(const Vector sourceVector, const Vector targetVector);
27
+
28
+ // Linearly interpolate between two transforms - uses NLerp for rotations
29
+ static Transform Lerp(const Transform& from, const Transform& to, float t);
30
+
31
+ // Spherically interpolate between two transforms - uses SLerp for rotations
32
+ static Transform Slerp(const Transform& from, const Transform& to, float t);
33
+
34
+ // Spherically interpolate between two transforms - uses FastSLerp (SLerp approximation) for rotations
35
+ static Transform FastSlerp(const Transform& from, const Transform& to, float t);
36
+
37
+ // Calculate a delta transform that you can concatenate to the
38
+ // 'from' transform to get the 'to' transform. Properly handles the non-uniform scaling case.
39
+ static Transform Delta(const Transform& from, const Transform& to);
40
+
41
+ // Calculates a delta transform that you can concatenate to the
42
+ // 'from' transform to get the 'to' transform (ignoring scale)
43
+ static Transform DeltaNoScale(const Transform& from, const Transform& to);
44
+
45
+ static void DirectlySetRotation(Transform& transform, Quaternion&& rotation);
46
+ static void DirectlySetRotation(Transform& transform, const Quaternion& rotation);
47
+ static void DirectlySetTranslationScale(Transform& transform, Vector&& translationScale);
48
+ static void DirectlySetTranslationScale(Transform& transform, const Vector& translationScale);
49
+
50
+ public:
51
+
52
+ Transform() = default;
53
+
54
+ explicit Transform(NoInit_t);
55
+ explicit Transform(const Matrix& m);
56
+ explicit Transform(
57
+ const Quaternion& rotation,
58
+ const Vector& translation = Vector(0, 0, 0, 0),
59
+ float scale = 1.0f);
60
+ explicit Transform(const AxisAngle& rotation);
61
+
62
+ Matrix ToMatrix() const;
63
+ Matrix ToMatrixNoScale() const;
64
+ EulerAngles ToEulerAngles() const;
65
+
66
+ Vector GetAxisX() const;
67
+ Vector GetAxisY() const;
68
+ Vector GetAxisZ() const;
69
+
70
+ Vector GetRightVector() const;
71
+ Vector GetForwardVector() const;
72
+ Vector GetUpVector() const;
73
+
74
+ bool IsIdentity() const;
75
+ bool IsRigidTransform() const;
76
+ void MakeRigidTransform();
77
+
78
+ //
79
+ // Inverse and Deltas
80
+ //
81
+
82
+ // Invert this transform.
83
+ // If you want a delta transform that you can
84
+ // concatenate, then you should use the 'Delta' functions
85
+ Transform& Inverse();
86
+
87
+ // Get the inverse of this transform.
88
+ // If you want a delta transform that you can
89
+ // concatenate, then you should use the 'Delta' functions
90
+ Transform GetInverse() const;
91
+
92
+ // Return the delta required to a given target
93
+ // transform (i.e., what do we need to add to reach that transform)
94
+ Transform GetDeltaToOther(const Transform& targetTransform) const;
95
+
96
+ // Return the delta relative from a given a start
97
+ // transform (i.e., how much do we differ from it)
98
+ Transform GetDeltaFromOther(const Transform& startTransform) const;
99
+
100
+ //
101
+ // Rotation
102
+
103
+ const Quaternion& GetRotation() const;
104
+ void SetRotation(const Quaternion& rotation);
105
+ void AddRotation(const Quaternion& delta);
106
+
107
+ //
108
+ // Translation
109
+ //
110
+
111
+ // Get the translation for this transform
112
+ // NOTE: you cannot rely on the W value as that will be the scale
113
+ const Vector& GetTranslation() const;
114
+
115
+ // Get the translation and scale for this transform
116
+ const Vector& GetTranslationAndScale() const;
117
+
118
+ // Set the translation
119
+ void SetTranslation(const Vector& newTranslation);
120
+
121
+ // Set the translation and scale simultaneously
122
+ void SetTranslationAndScale(const Vector& newTranslationScale);
123
+
124
+ // Add an offset to the current translation
125
+ void AddTranslation(const Vector& translationDelta);
126
+
127
+ // Get the translation as a homogeneous coordinates' vector (W=0)
128
+ Vector GetTranslationAsVector() const;
129
+
130
+ // Get the translation as a homogeneous coordinates' point (W=1)
131
+ Vector GetTranslationAsPoint() const;
132
+
133
+ //
134
+ // Scale
135
+ //
136
+
137
+ float GetScale() const;
138
+ Vector GetScaleVector() const;
139
+ Vector GetInverseScaleVector() const;
140
+ void SetScale(float uniformScale);
141
+ bool HasScale() const;
142
+ bool HasNegativeScale() const;
143
+
144
+ // This function will sanitize the scale values to remove any
145
+ // trailing values from scale factors i.e. 1.000000012 will be converted to 1
146
+ // This is primarily needed in import steps where scale values
147
+ // might be sampled from curves or have multiple conversions applied resulting in variance.
148
+ void SanitizeScaleValue();
149
+
150
+ //
151
+ // Transformations
152
+ //
153
+
154
+ Vector TranslateVector(const Vector& vector) const;
155
+ Vector ScaleVector(const Vector& vector) const;
156
+ Vector TransformPoint(const Vector& vector) const;
157
+ Vector TransformPointNoScale(const Vector& vector) const;
158
+
159
+ // Rotate a vector (same as TransformVectorNoScale)
160
+ Vector RotateVector(const Vector& vector) const;
161
+
162
+ // Rotate a vector (same as TransformVectorNoScale)
163
+ Vector TransformNormal(const Vector& vector) const;
164
+
165
+ // Unrotate a vector (same as InverseTransformVectorNoScale)
166
+ Vector InverseRotateVector(const Vector& vector) const;
167
+
168
+ // Invert the operation order when doing inverse transformation: first translation then rotation then scale
169
+ Vector InverseTransformPoint(const Vector& point) const;
170
+
171
+ // Invert the operation order when doing inverse transformation: first translation then rotation
172
+ Vector InverseTransformPointNoScale(const Vector& point) const;
173
+
174
+ // Applies scale and rotation to a vector (no translation)
175
+ Vector TransformVector(const Vector& vector) const;
176
+
177
+ // Rotate a vector
178
+ Vector TransformVectorNoScale(const Vector& vector) const;
179
+
180
+ // Invert the operation order when performing inverse transformation: first rotation then scale
181
+ Vector InverseTransformVector(const Vector& vector) const;
182
+
183
+ // Unrotate a vector
184
+ Vector InverseTransformVectorNoScale(const Vector& vector) const;
185
+
186
+ // WARNING: The results from multiplying transforms with shear or skew is ill-defined
187
+ Transform operator*(const Transform& rhs) const;
188
+
189
+ // WARNING: The results from multiplying transforms with shear or skew is ill-defined
190
+ Transform& operator*=(const Transform& rhs);
191
+
192
+ //
193
+ // Operators
194
+ //
195
+
196
+ bool IsNearEqual(
197
+ const Transform& rhs,
198
+ const Radians angleThreshold = Math::DegreesToRadians,
199
+ float translationScaleThreshold = Math::Epsilon) const;
200
+
201
+ // Exact equality
202
+ bool operator==(const Transform& rhs) const;
203
+
204
+ bool operator!=(const Transform& rhs) const;
205
+
206
+ private:
207
+
208
+ Quaternion m_rotation = Quaternion(0, 0, 0, 1);
209
+ Vector m_translationScale = Vector(0, 0, 0, 1);
210
+ };
211
+ }
212
+
213
+ #include "Transform.inl"
MotionCorrection/src/cpp/Math/Transform.inl ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Transform.h"
9
+
10
+ namespace Math
11
+ {
12
+ inline Transform Transform::FromRotation(const Quaternion& rotation)
13
+ {
14
+ return Transform(rotation);
15
+ }
16
+
17
+ inline Transform Transform::FromTranslation(const Vector& translation)
18
+ {
19
+ return Transform(Quaternion::Identity, translation);
20
+ }
21
+
22
+ inline Transform Transform::FromScale(float uniformScale)
23
+ {
24
+ return Transform(Quaternion::Identity, Vector::Zero, uniformScale);
25
+ }
26
+
27
+ inline Transform Transform::FromTranslationAndScale(const Vector& translation, float uniformScale)
28
+ {
29
+ return Transform(Quaternion::Identity, translation, uniformScale);
30
+ }
31
+
32
+ inline Transform Transform::FromRotationBetweenVectors(const Vector sourceVector, const Vector targetVector)
33
+ {
34
+ return Transform(Quaternion::FromRotationBetweenNormalizedVectors(sourceVector, targetVector));
35
+ }
36
+
37
+ inline Transform Transform::Lerp(const Transform& from, const Transform& to, float t)
38
+ {
39
+ Quaternion const rotation = Quaternion::NLerp(Quaternion(from.m_rotation), Quaternion(to.m_rotation), t);
40
+ Vector const translationAndScale = Vector::Lerp(from.m_translationScale, to.m_translationScale, t);
41
+
42
+ Transform lerped(NoInit);
43
+ lerped.m_rotation = rotation;
44
+ lerped.m_translationScale = translationAndScale;
45
+ return lerped;
46
+ }
47
+
48
+ inline Transform Transform::Slerp(const Transform& from, const Transform& to, float t)
49
+ {
50
+ Quaternion const rotation = Quaternion::SLerp(Quaternion(from.m_rotation), Quaternion(to.m_rotation), t);
51
+ Vector const translationAndScale = Vector::Lerp(Vector(from.m_translationScale), Vector(to.m_translationScale), t);
52
+
53
+ Transform lerped(NoInit);
54
+ lerped.m_rotation = rotation;
55
+ lerped.m_translationScale = translationAndScale;
56
+ return lerped;
57
+ }
58
+
59
+ inline Transform Transform::FastSlerp(const Transform& from, const Transform& to, float t)
60
+ {
61
+ Quaternion const rotation = Quaternion::FastSLerp(Quaternion(from.m_rotation), Quaternion(to.m_rotation), t);
62
+ Vector const translationAndScale = Vector::Lerp(Vector(from.m_translationScale), Vector(to.m_translationScale), t);
63
+
64
+ Transform lerped(NoInit);
65
+ lerped.m_rotation = rotation;
66
+ lerped.m_translationScale = translationAndScale;
67
+ return lerped;
68
+ }
69
+
70
+ inline Transform Transform::Delta(const Transform& from, const Transform& to)
71
+ {
72
+ ASSERT(from.m_rotation.IsNormalized() && to.m_rotation.IsNormalized());
73
+ ASSERT(!from.m_translationScale.IsW0() && !to.m_translationScale.IsW0());
74
+
75
+ Transform result;
76
+
77
+ Vector const inverseScale = from.GetInverseScaleVector();
78
+ Vector const deltaScale = to.GetScaleVector() * inverseScale;
79
+
80
+ // If we have negative scaling, we need to use matrices to calculate the deltas
81
+ Vector const minScale = Vector::Min(from.m_translationScale.GetSplatW(), to.m_translationScale.GetSplatW());
82
+ if (minScale.IsAnyLessThan(Vector::Zero))
83
+ {
84
+ // Multiply the transforms using matrices to get the correct rotation and then remove the scale;
85
+ Matrix const toMtx = to.ToMatrix();
86
+ Matrix const fromMtx = from.ToMatrix();
87
+ Matrix resultMtx = toMtx * fromMtx.GetInverse();
88
+ resultMtx.RemoveScaleFast();
89
+
90
+ // Apply back the signs from the final scale
91
+ Vector const sign = deltaScale.GetSign();
92
+ resultMtx[0] *= sign.GetSplatX();
93
+ resultMtx[1] *= sign.GetSplatY();
94
+ resultMtx[2] *= sign.GetSplatZ();
95
+
96
+ result.m_rotation = resultMtx.GetRotation();
97
+ ASSERT(result.m_rotation.IsNormalized());
98
+ result.m_translationScale = Vector::Select(resultMtx.GetTranslation(), deltaScale, Vector::Select0001);
99
+ }
100
+ else
101
+ {
102
+ Quaternion const fromInverseRotation = from.m_rotation.GetInverse();
103
+ result.m_rotation = to.m_rotation * fromInverseRotation;
104
+
105
+ Vector const deltaTranslation = to.m_translationScale - from.m_translationScale;
106
+ Vector const translation = fromInverseRotation.RotateVector(deltaTranslation) * inverseScale;
107
+ result.m_translationScale = Vector::Select(translation, deltaScale, Vector::Select0001);
108
+ }
109
+
110
+ return result;
111
+ }
112
+
113
+ inline Transform Transform::DeltaNoScale(const Transform& from, const Transform& to)
114
+ {
115
+ Quaternion const inverseFromRotation = from.m_rotation.GetInverse();
116
+ Vector const deltaTranslation = to.GetTranslation() - from.GetTranslation();
117
+
118
+ Transform delta;
119
+ delta.m_rotation = to.m_rotation * inverseFromRotation;
120
+ delta.m_translationScale = inverseFromRotation.RotateVector(deltaTranslation).GetWithW1();
121
+ return delta;
122
+ }
123
+
124
+ inline void Transform::DirectlySetRotation(Transform& transform, Quaternion&& rotation)
125
+ {
126
+ transform.m_rotation = rotation;
127
+ }
128
+
129
+ inline void Transform::DirectlySetRotation(Transform& transform, const Quaternion& rotation)
130
+ {
131
+ transform.m_rotation = rotation;
132
+ }
133
+
134
+ inline void Transform::DirectlySetTranslationScale(Transform& transform, Vector&& translationScale)
135
+ {
136
+ transform.m_translationScale = translationScale;
137
+ }
138
+
139
+ inline void Transform::DirectlySetTranslationScale(Transform& transform, const Vector& translationScale)
140
+ {
141
+ transform.m_translationScale = translationScale;
142
+ }
143
+
144
+ inline Transform::Transform(NoInit_t)
145
+ : m_rotation(NoInit)
146
+ , m_translationScale(NoInit)
147
+ {
148
+ }
149
+
150
+ inline Transform::Transform(const Matrix& m)
151
+ {
152
+ Vector mTranslation, mScale;
153
+ m.Decompose(m_rotation, mTranslation, mScale);
154
+ ASSERT(Math::IsNearEqual(mScale.GetX(), mScale.GetY()) && Math::IsNearEqual(mScale.GetY(),mScale.GetZ()));
155
+ m_translationScale = Vector::Select(mTranslation, mScale, Vector::Select0001);
156
+ }
157
+
158
+ inline Transform::Transform(const Quaternion& rotation, const Vector& translation, float scale)
159
+ : m_rotation(rotation)
160
+ , m_translationScale(Vector::Select(translation, Vector(scale), Vector::Select0001))
161
+ {
162
+ }
163
+
164
+ inline Transform::Transform(const AxisAngle& rotation)
165
+ : m_rotation(rotation)
166
+ , m_translationScale(Vector::UnitW)
167
+ {
168
+ }
169
+
170
+ inline Matrix Transform::ToMatrix() const
171
+ {
172
+ return Matrix(m_rotation, m_translationScale.GetWithW1(), m_translationScale.GetSplatW());
173
+ }
174
+
175
+ inline Matrix Transform::ToMatrixNoScale() const
176
+ {
177
+ return Matrix(m_rotation, m_translationScale.GetWithW1(), Vector::One);
178
+ }
179
+
180
+ inline EulerAngles Transform::ToEulerAngles() const
181
+ {
182
+ return m_rotation.ToEulerAngles();
183
+ }
184
+
185
+ inline Vector Transform::GetAxisX() const
186
+ {
187
+ return m_rotation.RotateVector(Vector::UnitX);
188
+ }
189
+
190
+ inline Vector Transform::GetAxisY() const
191
+ {
192
+ return m_rotation.RotateVector(Vector::UnitY);
193
+ }
194
+
195
+ inline Vector Transform::GetAxisZ() const
196
+ {
197
+ return m_rotation.RotateVector(Vector::UnitZ);
198
+ }
199
+
200
+ inline Vector Transform::GetRightVector() const
201
+ {
202
+ return m_rotation.RotateVector(Vector::WorldRight);
203
+ }
204
+
205
+ inline Vector Transform::GetForwardVector() const
206
+ {
207
+ return m_rotation.RotateVector(Vector::WorldForward);
208
+ }
209
+
210
+ inline Vector Transform::GetUpVector() const
211
+ {
212
+ return m_rotation.RotateVector(Vector::WorldUp);
213
+ }
214
+
215
+ inline bool Transform::IsIdentity() const
216
+ {
217
+ return m_rotation.IsIdentity() && m_translationScale.IsEqual4(Vector::UnitW);
218
+ }
219
+
220
+ inline bool Transform::IsRigidTransform() const
221
+ {
222
+ return GetScale() == 1.0f;
223
+ }
224
+
225
+ inline void Transform::MakeRigidTransform()
226
+ {
227
+ SetScale(1.0f);
228
+ }
229
+
230
+ inline Transform& Transform::Inverse()
231
+ {
232
+ ASSERT(!m_translationScale.IsW0());
233
+
234
+ Quaternion const inverseRotation = m_rotation.GetInverse();
235
+ m_rotation = inverseRotation;
236
+
237
+ Vector const inverseScale = GetInverseScaleVector();
238
+ Vector const inverselyScaledTranslation = inverseScale * m_translationScale.GetWithW0();
239
+ Vector const inverselyRotatedTranslation = inverseRotation.RotateVector(inverselyScaledTranslation);
240
+ Vector const inverseTranslation = inverselyRotatedTranslation.GetNegated().SetW0();
241
+
242
+ m_translationScale = Vector::Select(inverseTranslation, inverseScale, Vector::Select0001);
243
+
244
+ return *this;
245
+ }
246
+
247
+ inline Transform Transform::GetInverse() const
248
+ {
249
+ Transform inverse = *this;
250
+ return inverse.Inverse();
251
+ }
252
+
253
+ inline Transform Transform::GetDeltaToOther(const Transform& targetTransform) const
254
+ {
255
+ return Transform::Delta(*this, targetTransform);
256
+ }
257
+
258
+ inline Transform Transform::GetDeltaFromOther(const Transform& startTransform) const
259
+ {
260
+ return Transform::Delta(startTransform, *this);
261
+ }
262
+
263
+ inline const Quaternion& Transform::GetRotation() const
264
+ {
265
+ return m_rotation;
266
+ }
267
+
268
+ inline void Transform::SetRotation(const Quaternion& rotation)
269
+ {
270
+ ASSERT(rotation.IsNormalized());
271
+ m_rotation = rotation;
272
+ }
273
+
274
+ inline void Transform::AddRotation(const Quaternion& delta)
275
+ {
276
+ ASSERT(delta.IsNormalized());
277
+ m_rotation = delta * m_rotation;
278
+ }
279
+
280
+ inline const Vector& Transform::GetTranslation() const
281
+ {
282
+ return m_translationScale;
283
+ }
284
+
285
+ inline const Vector& Transform::GetTranslationAndScale() const
286
+ {
287
+ return m_translationScale;
288
+ }
289
+
290
+ inline void Transform::SetTranslation(const Vector& newTranslation)
291
+ {
292
+ m_translationScale = Vector::Select(newTranslation, m_translationScale, Vector::Select0001);
293
+ }
294
+
295
+ inline void Transform::SetTranslationAndScale(const Vector& newTranslationScale)
296
+ {
297
+ ASSERT(newTranslationScale.GetW() != 0.0f);
298
+ m_translationScale = newTranslationScale;
299
+ }
300
+
301
+ inline void Transform::AddTranslation(const Vector& translationDelta)
302
+ {
303
+ m_translationScale += translationDelta.GetWithW0();
304
+ }
305
+
306
+ inline Vector Transform::GetTranslationAsVector() const
307
+ {
308
+ return m_translationScale.GetWithW0();
309
+ }
310
+
311
+ inline Vector Transform::GetTranslationAsPoint() const
312
+ {
313
+ return m_translationScale.GetWithW1();
314
+ }
315
+
316
+ inline float Transform::GetScale() const
317
+ {
318
+ return m_translationScale.GetW();
319
+ }
320
+
321
+ inline Vector Transform::GetScaleVector() const
322
+ {
323
+ return m_translationScale.GetSplatW();
324
+ }
325
+
326
+ inline Vector Transform::GetInverseScaleVector() const
327
+ {
328
+ return m_translationScale.GetSplatW().GetInverse();
329
+ }
330
+
331
+ inline void Transform::SetScale(float uniformScale)
332
+ {
333
+ m_translationScale.SetW(uniformScale);
334
+ }
335
+
336
+ inline bool Transform::HasScale() const
337
+ {
338
+ return m_translationScale.GetW() != 1.0f;
339
+ }
340
+
341
+ inline bool Transform::HasNegativeScale() const
342
+ {
343
+ return m_translationScale.GetW() < 0.0f;
344
+ }
345
+
346
+ inline Vector Transform::TranslateVector(const Vector& vector) const
347
+ {
348
+ return vector + m_translationScale.GetWithW0();
349
+ }
350
+
351
+ inline Vector Transform::ScaleVector(const Vector& vector) const
352
+ {
353
+ return vector * GetScaleVector();
354
+ }
355
+
356
+ inline Vector Transform::TransformPoint(const Vector& point) const
357
+ {
358
+ ASSERT(!m_translationScale.IsW0());
359
+ Vector transformedPoint = point * m_translationScale.GetSplatW();
360
+ transformedPoint = (m_translationScale + m_rotation.RotateVector(transformedPoint)).GetWithW0();
361
+ return transformedPoint;
362
+ }
363
+
364
+ inline Vector Transform::TransformPointNoScale(const Vector& point) const
365
+ {
366
+ Vector transformedPoint = (m_translationScale + m_rotation.RotateVector(point)).GetWithW0();;
367
+ return transformedPoint;
368
+ }
369
+
370
+ inline Vector Transform::RotateVector(const Vector& vector) const
371
+ {
372
+ return m_rotation.RotateVector(vector);
373
+ }
374
+
375
+ inline Vector Transform::TransformNormal(const Vector& vector) const
376
+ {
377
+ return RotateVector(vector);
378
+ }
379
+
380
+ inline Vector Transform::InverseRotateVector(const Vector& vector) const
381
+ {
382
+ return m_rotation.RotateVectorInverse(vector);
383
+ }
384
+
385
+ inline Vector Transform::InverseTransformPoint(const Vector& point) const
386
+ {
387
+ ASSERT(!m_translationScale.IsW0());
388
+ Vector const shiftedPoint = point - m_translationScale;
389
+ Vector const unrotatedShiftedPoint = m_rotation.RotateVectorInverse(shiftedPoint);
390
+ Vector const inverseScale = GetInverseScaleVector();
391
+ Vector const result = unrotatedShiftedPoint * inverseScale;
392
+ return result;
393
+ }
394
+
395
+ inline Vector Transform::InverseTransformPointNoScale(const Vector& point) const
396
+ {
397
+ Vector const shiftedPoint = point - m_translationScale;
398
+ Vector const unrotatedShiftedPoint = m_rotation.RotateVectorInverse(shiftedPoint);
399
+ return unrotatedShiftedPoint;
400
+ }
401
+
402
+ inline Vector Transform::TransformVector(const Vector& vector) const
403
+ {
404
+ ASSERT(!m_translationScale.IsW0());
405
+ Vector transformedVector = vector * GetScaleVector();
406
+ transformedVector = m_rotation.RotateVector(transformedVector);
407
+ return transformedVector;
408
+ }
409
+
410
+ inline Vector Transform::TransformVectorNoScale(const Vector& vector) const
411
+ {
412
+ return RotateVector(vector);
413
+ }
414
+
415
+ inline Vector Transform::InverseTransformVector(const Vector& vector) const
416
+ {
417
+ ASSERT(!m_translationScale.IsW0());
418
+ Vector const unrotatedVector = m_rotation.RotateVectorInverse(vector);
419
+ Vector const inverseScale = GetInverseScaleVector();
420
+ Vector const result = unrotatedVector * inverseScale;
421
+ return result;
422
+ }
423
+
424
+ inline Vector Transform::InverseTransformVectorNoScale(const Vector& vector) const
425
+ {
426
+ return m_rotation.RotateVectorInverse(vector);
427
+ }
428
+
429
+ inline Transform Transform::operator*(const Transform& rhs) const
430
+ {
431
+ Transform transform = *this;
432
+ transform *= rhs;
433
+ return transform;
434
+ }
435
+
436
+ inline Transform& Transform::operator*=(const Transform& rhs)
437
+ {
438
+ Vector const scale = GetScaleVector();
439
+ Vector const rhsScale = rhs.GetScaleVector();
440
+ Vector const minScale = Vector::Min(scale, rhsScale);
441
+ Vector const finalScale = scale * rhsScale;
442
+
443
+ if (minScale.IsAnyLessThan(Vector::Zero))
444
+ {
445
+ // Multiply the transforms using matrices to
446
+ // get the correct rotation and then remove the scale;
447
+ Matrix const lhsMtx = ToMatrix();
448
+ Matrix const rhsMtx = rhs.ToMatrix();
449
+ Matrix resultMtx = lhsMtx * rhsMtx;
450
+ resultMtx.RemoveScaleFast();
451
+
452
+ // Apply back the signs from the final scale
453
+ Vector const sign = finalScale.GetSign();
454
+ resultMtx[0] *= sign.GetSplatX();
455
+ resultMtx[1] *= sign.GetSplatY();
456
+ resultMtx[2] *= sign.GetSplatZ();
457
+
458
+ m_rotation = resultMtx.GetRotation();
459
+ ASSERT(m_rotation.IsNormalized());
460
+ m_translationScale = Vector::Select(resultMtx.GetTranslation(), finalScale, Vector::Select0001);
461
+ }
462
+ else
463
+ {
464
+ // Normal case
465
+ m_rotation = m_rotation * rhs.m_rotation;
466
+ m_rotation.Normalize();
467
+ Vector const translation = rhs.m_rotation.RotateVector(m_translationScale * rhsScale) + rhs.m_translationScale;
468
+ m_translationScale = Vector::Select(translation, finalScale, Vector::Select0001);
469
+ }
470
+
471
+ return *this;
472
+ }
473
+
474
+ inline bool Transform::IsNearEqual(const Transform& rhs, const Radians angleThreshold, float translationScaleThreshold) const
475
+ {
476
+ if (!m_rotation.IsNearEqual(rhs.m_rotation, angleThreshold))
477
+ {
478
+ return false;
479
+ }
480
+
481
+ if (!m_translationScale.IsNearEqual4(rhs.m_translationScale, translationScaleThreshold))
482
+ {
483
+ return false;
484
+ }
485
+
486
+ return true;
487
+ }
488
+
489
+ inline bool Transform::operator==(const Transform& rhs) const
490
+ {
491
+ if (m_translationScale != rhs.m_translationScale)
492
+ {
493
+ return false;
494
+ }
495
+
496
+ if (m_rotation != rhs.m_rotation)
497
+ {
498
+ return false;
499
+ }
500
+
501
+ return true;
502
+ }
503
+
504
+ inline bool Transform::operator!=(const Transform& rhs) const
505
+ {
506
+ return !operator==(rhs);
507
+ }
508
+ }
MotionCorrection/src/cpp/Math/Types.cpp ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "Types.h"
7
+
8
+ Int2 const Int2::Zero = Int2( 0, 0 );
9
+
10
+ Int4 const Int4::Zero = Int4( 0, 0, 0, 0 );
11
+ Int4 const Int4::MinusOne = Int4( -1, -1, -1, -1 );
12
+
13
+ Float2 const Float2::Zero = Float2( 0, 0 );
14
+ Float2 const Float2::One = Float2( 1, 1 );
15
+ Float2 const Float2::UnitX = Float2( 1, 0 );
16
+ Float2 const Float2::UnitY = Float2( 0, 1 );
17
+
18
+ Float3 const Float3::Zero = Float3( 0, 0, 0 );
19
+ Float3 const Float3::One = Float3( 1, 1, 1 );
20
+ Float3 const Float3::UnitX = Float3( 1, 0, 0 );
21
+ Float3 const Float3::UnitY = Float3( 0, 1, 0 );
22
+ Float3 const Float3::UnitZ = Float3( 0, 0, 1 );
23
+
24
+ Float3 const Float3::WorldForward = Float3( 0, -1, 0 );
25
+ Float3 const Float3::WorldUp = Float3( 0, 0, 1 );
26
+ Float3 const Float3::WorldRight = Float3( -1, 0, 0 );
27
+
28
+ Float4 const Float4::Zero = Float4( 0, 0, 0, 0 );
29
+ Float4 const Float4::One = Float4( 1, 1, 1, 1 );
30
+ Float4 const Float4::UnitX = Float4( 1, 0, 0, 0 );
31
+ Float4 const Float4::UnitY = Float4( 0, 1, 0, 0 );
32
+ Float4 const Float4::UnitZ = Float4( 0, 0, 1, 0 );
33
+ Float4 const Float4::UnitW = Float4( 0, 0, 0, 1 );
34
+
35
+ Float4 const Float4::WorldForward = Float4( 0, -1, 0, 0 );
36
+ Float4 const Float4::WorldUp = Float4( 0, 0, 1, 0 );
37
+ Float4 const Float4::WorldRight = Float4( -1, 0, 0, 0 );
38
+
39
+ Radians const Radians::Pi = Radians( Math::Pi );
40
+ Radians const Radians::TwoPi = Radians( Math::TwoPi );
41
+ Radians const Radians::OneDivPi = Radians( Math::OneDivPi );
42
+ Radians const Radians::OneDivTwoPi = Radians( Math::OneDivTwoPi );
43
+ Radians const Radians::PiDivTwo = Radians( Math::PiDivTwo );
44
+ Radians const Radians::PiDivFour = Radians( Math::PiDivFour );
MotionCorrection/src/cpp/Math/Types.h ADDED
@@ -0,0 +1,808 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Scalar.h"
9
+
10
+ enum NoInit_t { NoInit };
11
+ enum ZeroInit_t { ZeroInit };
12
+ enum IdentityInit_t { IdentityInit };
13
+
14
+ enum class Axis : uint8_t
15
+ {
16
+ X = 0,
17
+ Y,
18
+ Z,
19
+ NegX,
20
+ NegY,
21
+ NegZ
22
+ };
23
+
24
+ struct Float2;
25
+ struct Float3;
26
+ struct Float4;
27
+
28
+ struct Int2
29
+ {
30
+ static Int2 const Zero;
31
+
32
+ public:
33
+
34
+ inline Int2() {}
35
+ inline Int2( ZeroInit_t ) : m_x( 0 ), m_y( 0 ) {}
36
+ inline Int2( Float2 const& v );
37
+ inline explicit Int2( int32_t v ) : m_x( v ), m_y( v ) {}
38
+ inline explicit Int2( int32_t ix, int32_t iy ) : m_x( ix ), m_y( iy ) {}
39
+
40
+ inline bool IsZero() const { return *this == Zero; }
41
+
42
+ inline int32_t& operator[]( uint32_t i ) { return ( (int32_t*) this )[i]; }
43
+ inline int32_t const& operator[]( uint32_t i ) const { return ( (int32_t*) this )[i]; }
44
+
45
+ inline bool operator==( Int2 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y; }
46
+ inline bool operator!=( Int2 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y; }
47
+
48
+ inline Int2 operator+( Int2 const& rhs ) const { return Int2( m_x + rhs.m_x, m_y + rhs.m_y ); }
49
+ inline Int2 operator-( Int2 const& rhs ) const { return Int2( m_x - rhs.m_x, m_y - rhs.m_y ); }
50
+ inline Int2 operator*( Int2 const& rhs ) const { return Int2( m_x * rhs.m_x, m_y * rhs.m_y ); }
51
+ inline Int2 operator/( Int2 const& rhs ) const { return Int2( m_x / rhs.m_x, m_y / rhs.m_y ); }
52
+
53
+ inline Int2& operator+=( int32_t const& rhs ) { m_x += rhs; m_y += rhs; return *this; }
54
+ inline Int2& operator-=( int32_t const& rhs ) { m_x -= rhs; m_y -= rhs; return *this; }
55
+ inline Int2& operator*=( int32_t const& rhs ) { m_x *= rhs; m_y *= rhs; return *this; }
56
+ inline Int2& operator/=( int32_t const& rhs ) { m_x /= rhs; m_y /= rhs; return *this; }
57
+
58
+ // Component wise operation
59
+ inline Int2 operator+( int32_t const& rhs ) const { return Int2( m_x + rhs, m_y + rhs ); }
60
+ inline Int2 operator-( int32_t const& rhs ) const { return Int2( m_x - rhs, m_y - rhs ); }
61
+ inline Int2 operator*( int32_t const& rhs ) const { return Int2( m_x * rhs, m_y * rhs ); }
62
+ inline Int2 operator/( int32_t const& rhs ) const { return Int2( m_x / rhs, m_y / rhs ); }
63
+
64
+ inline Int2& operator+=( Int2 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; return *this; }
65
+ inline Int2& operator-=( Int2 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; return *this; }
66
+ inline Int2& operator*=( Int2 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; return *this; }
67
+ inline Int2& operator/=( Int2 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; return *this; }
68
+
69
+ public:
70
+
71
+ int32_t m_x, m_y;
72
+ };
73
+
74
+ struct Int3
75
+ {
76
+ static Int3 const Zero;
77
+
78
+ public:
79
+
80
+ inline Int3() {}
81
+ inline Int3( ZeroInit_t ) : m_x( 0 ), m_y( 0 ), m_z( 0 ) {}
82
+ inline Int3( Float3 const& v );
83
+ inline explicit Int3( int32_t v ) : m_x( v ), m_y( v ), m_z( v ) {}
84
+ inline explicit Int3( int32_t ix, int32_t iy, int32_t iz ) : m_x( ix ), m_y( iy ), m_z( iz ) {}
85
+
86
+ inline bool IsZero() const { return *this == Zero; }
87
+
88
+ inline int32_t& operator[]( uint32_t i ) { return ( (int32_t*) this )[i]; }
89
+ inline int32_t const& operator[]( uint32_t i ) const { return ( (int32_t*) this )[i]; }
90
+
91
+ inline bool operator==( Int3 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y && m_z == rhs.m_z; }
92
+ inline bool operator!=( Int3 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y || m_z != rhs.m_z; }
93
+
94
+ inline Int3 operator+( Int3 const& rhs ) const { return Int3( m_x + rhs.m_x, m_y + rhs.m_y, m_z + rhs.m_z ); }
95
+ inline Int3 operator-( Int3 const& rhs ) const { return Int3( m_x - rhs.m_x, m_y - rhs.m_y, m_z - rhs.m_z ); }
96
+ inline Int3 operator*( Int3 const& rhs ) const { return Int3( m_x * rhs.m_x, m_y * rhs.m_y, m_z * rhs.m_z ); }
97
+ inline Int3 operator/( Int3 const& rhs ) const { return Int3( m_x / rhs.m_x, m_y / rhs.m_y, m_z / rhs.m_z ); }
98
+
99
+ inline Int3& operator+=( int32_t const& rhs ) { m_x += rhs; m_y += rhs; m_z += rhs; return *this; }
100
+ inline Int3& operator-=( int32_t const& rhs ) { m_x -= rhs; m_y -= rhs; m_z -= rhs; return *this; }
101
+ inline Int3& operator*=( int32_t const& rhs ) { m_x *= rhs; m_y *= rhs; m_z *= rhs; return *this; }
102
+ inline Int3& operator/=( int32_t const& rhs ) { m_x /= rhs; m_y /= rhs; m_z /= rhs; return *this; }
103
+
104
+ // Component wise operation
105
+ inline Int3 operator+( int32_t const& rhs ) const { return Int3( m_x + rhs, m_y + rhs, m_z + rhs ); }
106
+ inline Int3 operator-( int32_t const& rhs ) const { return Int3( m_x - rhs, m_y - rhs, m_z - rhs ); }
107
+ inline Int3 operator*( int32_t const& rhs ) const { return Int3( m_x * rhs, m_y * rhs, m_z * rhs ); }
108
+ inline Int3 operator/( int32_t const& rhs ) const { return Int3( m_x / rhs, m_y / rhs, m_z / rhs ); }
109
+
110
+ inline Int3& operator+=( Int3 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; m_z += rhs.m_z; return *this; }
111
+ inline Int3& operator-=( Int3 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; m_z -= rhs.m_z; return *this; }
112
+ inline Int3& operator*=( Int3 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; m_z *= rhs.m_z; return *this; }
113
+ inline Int3& operator/=( Int3 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; m_z /= rhs.m_z; return *this; }
114
+
115
+ public:
116
+
117
+ int32_t m_x, m_y, m_z;
118
+ };
119
+
120
+ struct Int4
121
+ {
122
+ static Int4 const Zero;
123
+ static Int4 const MinusOne;
124
+
125
+ public:
126
+
127
+ inline Int4() {}
128
+ inline Int4( ZeroInit_t ) : m_x( 0 ), m_y( 0 ), m_z( 0 ), m_w( 0 ) {}
129
+ inline explicit Int4( int32_t v ) : m_x( v ), m_y( v ), m_z( v ), m_w( v ) {}
130
+ inline explicit Int4( int32_t ix, int32_t iy, int32_t iz, int32_t iw ) : m_x( ix ), m_y( iy ), m_z( iz ), m_w( iw ) {}
131
+
132
+ inline bool IsZero() const { return *this == Zero; }
133
+
134
+ inline int32_t& operator[]( uint32_t i ) { return ( (int32_t*) this )[i]; }
135
+ inline int32_t const& operator[]( uint32_t i ) const { return ( (int32_t*) this )[i]; }
136
+
137
+ inline bool operator==( Int4 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y && m_z == rhs.m_z && m_w == rhs.m_w; }
138
+ inline bool operator!=( Int4 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y || m_z != rhs.m_z || m_w != rhs.m_w; }
139
+
140
+ inline Int4 operator+( int32_t const& rhs ) const { return Int4( m_x + rhs, m_y + rhs, m_z + rhs, m_w + rhs ); }
141
+ inline Int4 operator-( int32_t const& rhs ) const { return Int4( m_x - rhs, m_y - rhs, m_z - rhs, m_w - rhs ); }
142
+ inline Int4 operator*( int32_t const& rhs ) const { return Int4( m_x * rhs, m_y * rhs, m_z * rhs, m_w * rhs ); }
143
+ inline Int4 operator/( int32_t const& rhs ) const { return Int4( m_x / rhs, m_y / rhs, m_z / rhs, m_w / rhs ); }
144
+
145
+ inline Int4& operator+=( int32_t const& rhs ) { m_x += rhs; m_y += rhs; m_z += rhs; m_w += rhs; return *this; }
146
+ inline Int4& operator-=( int32_t const& rhs ) { m_x -= rhs; m_y -= rhs; m_z -= rhs; m_w -= rhs; return *this; }
147
+ inline Int4& operator*=( int32_t const& rhs ) { m_x *= rhs; m_y *= rhs; m_z *= rhs; m_w *= rhs; return *this; }
148
+ inline Int4& operator/=( int32_t const& rhs ) { m_x /= rhs; m_y /= rhs; m_z /= rhs; m_w /= rhs; return *this; }
149
+
150
+ // Component wise operation
151
+ inline Int4 operator+( Int4 const& rhs ) const { return Int4( m_x + rhs.m_x, m_y + rhs.m_y, m_z + rhs.m_z, m_w + rhs.m_w ); }
152
+ inline Int4 operator-( Int4 const& rhs ) const { return Int4( m_x - rhs.m_x, m_y - rhs.m_y, m_z - rhs.m_z, m_w - rhs.m_w ); }
153
+ inline Int4 operator*( Int4 const& rhs ) const { return Int4( m_x * rhs.m_x, m_y * rhs.m_y, m_z * rhs.m_z, m_w * rhs.m_w ); }
154
+ inline Int4 operator/( Int4 const& rhs ) const { return Int4( m_x / rhs.m_x, m_y / rhs.m_y, m_z / rhs.m_z, m_w / rhs.m_w ); }
155
+
156
+ inline Int4& operator+=( Int4 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; m_z += rhs.m_z; m_w += rhs.m_w; return *this; }
157
+ inline Int4& operator-=( Int4 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; m_z -= rhs.m_z; m_w -= rhs.m_w; return *this; }
158
+ inline Int4& operator*=( Int4 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; m_z *= rhs.m_z; m_w *= rhs.m_w; return *this; }
159
+ inline Int4& operator/=( Int4 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; m_z /= rhs.m_z; m_w /= rhs.m_w; return *this; }
160
+
161
+ public:
162
+
163
+ int32_t m_x, m_y, m_z, m_w;
164
+ };
165
+
166
+ struct Float2
167
+ {
168
+ static Float2 const Zero;
169
+ static Float2 const One;
170
+ static Float2 const UnitX;
171
+ static Float2 const UnitY;
172
+
173
+ public:
174
+
175
+ inline Float2() {}
176
+ FORCE_INLINE Float2( ZeroInit_t ) : m_x( 0 ), m_y( 0 ) {}
177
+ FORCE_INLINE explicit Float2( float v ) : m_x( v ), m_y( v ) {}
178
+ FORCE_INLINE explicit Float2( float ix, float iy ) : m_x( ix ), m_y( iy ) {}
179
+ FORCE_INLINE explicit Float2( int32_t ix, int32_t iy ) : m_x( (float) ix ), m_y( (float) iy ) {}
180
+ inline explicit Float2( Int2 const& v ) : m_x( (float) v.m_x ), m_y( (float) v.m_y ) {}
181
+ inline explicit Float2( Float3 const& v );
182
+ inline explicit Float2( Float4 const& v );
183
+
184
+ inline bool IsZero() const { return *this == Zero; }
185
+
186
+ inline float& operator[]( uint32_t i ) { return ( (float*) this )[i]; }
187
+ inline float const& operator[]( uint32_t i ) const { return ( (float*) this )[i]; }
188
+
189
+ FORCE_INLINE Float2 operator-() const { return Float2( -m_x, -m_y ); }
190
+
191
+ inline bool operator==( Float2 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y; }
192
+ inline bool operator!=( Float2 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y; }
193
+
194
+ inline Float2 operator+( Float2 const& rhs ) const { return Float2( m_x + rhs.m_x, m_y + rhs.m_y ); }
195
+ inline Float2 operator-( Float2 const& rhs ) const { return Float2( m_x - rhs.m_x, m_y - rhs.m_y ); }
196
+ inline Float2 operator*( Float2 const& rhs ) const { return Float2( m_x * rhs.m_x, m_y * rhs.m_y ); }
197
+ inline Float2 operator/( Float2 const& rhs ) const { return Float2( m_x / rhs.m_x, m_y / rhs.m_y ); }
198
+
199
+ inline Float2 operator+( float const& rhs ) const { return Float2( m_x + rhs, m_y + rhs ); }
200
+ inline Float2 operator-( float const& rhs ) const { return Float2( m_x - rhs, m_y - rhs ); }
201
+ inline Float2 operator*( float const& rhs ) const { return Float2( m_x * rhs, m_y * rhs ); }
202
+ inline Float2 operator/( float const& rhs ) const { return Float2( m_x / rhs, m_y / rhs ); }
203
+
204
+ inline Float2& operator+=( Float2 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; return *this; }
205
+ inline Float2& operator-=( Float2 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; return *this; }
206
+ inline Float2& operator*=( Float2 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; return *this; }
207
+ inline Float2& operator/=( Float2 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; return *this; }
208
+
209
+ inline Float2& operator+=( float const& rhs ) { m_x += rhs; m_y += rhs; return *this; }
210
+ inline Float2& operator-=( float const& rhs ) { m_x -= rhs; m_y -= rhs; return *this; }
211
+ inline Float2& operator*=( float const& rhs ) { m_x *= rhs; m_y *= rhs; return *this; }
212
+ inline Float2& operator/=( float const& rhs ) { m_x /= rhs; m_y /= rhs; return *this; }
213
+
214
+ float m_x, m_y;
215
+ };
216
+
217
+ struct Float3
218
+ {
219
+ static Float3 const Zero;
220
+ static Float3 const One;
221
+ static Float3 const UnitX;
222
+ static Float3 const UnitY;
223
+ static Float3 const UnitZ;
224
+
225
+ static Float3 const WorldForward;
226
+ static Float3 const WorldUp;
227
+ static Float3 const WorldRight;
228
+
229
+ public:
230
+
231
+ inline Float3() {}
232
+ FORCE_INLINE Float3( ZeroInit_t ) : m_x( 0 ), m_y( 0 ), m_z( 0 ) {}
233
+ FORCE_INLINE explicit Float3( float v ) : m_x( v ), m_y( v ), m_z( v ) {}
234
+ FORCE_INLINE explicit Float3( float ix, float iy, float iz ) : m_x( ix ), m_y( iy ), m_z( iz ) {}
235
+ inline explicit Float3( Float2 const& v, float iz = 0.0f ) : m_x( v.m_x ), m_y( v.m_y ), m_z( iz ) {}
236
+ inline explicit Float3( Float4 const& v );
237
+
238
+ inline bool IsZero() const { return *this == Zero; }
239
+
240
+ inline float& operator[]( uint32_t i ) { return ( (float*) this )[i]; }
241
+ inline float const& operator[]( uint32_t i ) const { return ( (float*) this )[i]; }
242
+
243
+ FORCE_INLINE Float3 operator-() const { return Float3( -m_x, -m_y, -m_z ); }
244
+
245
+ inline bool operator==( Float3 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y && m_z == rhs.m_z; }
246
+ inline bool operator!=( Float3 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y || m_z != rhs.m_z; }
247
+
248
+ inline operator Float2() const { return Float2( m_x, m_y ); }
249
+
250
+ inline Float3 operator+( Float3 const& rhs ) const { return Float3( m_x + rhs.m_x, m_y + rhs.m_y, m_z + rhs.m_z ); }
251
+ inline Float3 operator-( Float3 const& rhs ) const { return Float3( m_x - rhs.m_x, m_y - rhs.m_y, m_z - rhs.m_z ); }
252
+ inline Float3 operator*( Float3 const& rhs ) const { return Float3( m_x * rhs.m_x, m_y * rhs.m_y, m_z * rhs.m_z ); }
253
+ inline Float3 operator/( Float3 const& rhs ) const { return Float3( m_x / rhs.m_x, m_y / rhs.m_y, m_z / rhs.m_z ); }
254
+
255
+ inline Float3 operator+( float const& rhs ) const { return Float3( m_x + rhs, m_y + rhs, m_z + rhs ); }
256
+ inline Float3 operator-( float const& rhs ) const { return Float3( m_x - rhs, m_y - rhs, m_z - rhs ); }
257
+ inline Float3 operator*( float const& rhs ) const { return Float3( m_x * rhs, m_y * rhs, m_z * rhs ); }
258
+ inline Float3 operator/( float const& rhs ) const { return Float3( m_x / rhs, m_y / rhs, m_z / rhs ); }
259
+
260
+ inline Float3& operator+=( Float3 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; m_z += rhs.m_z; return *this; }
261
+ inline Float3& operator-=( Float3 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; m_z -= rhs.m_z; return *this; }
262
+ inline Float3& operator*=( Float3 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; m_z *= rhs.m_z; return *this; }
263
+ inline Float3& operator/=( Float3 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; m_z /= rhs.m_z; return *this; }
264
+
265
+ inline Float3& operator+=( float const& rhs ) { m_x += rhs; m_y += rhs; m_z += rhs; return *this; }
266
+ inline Float3& operator-=( float const& rhs ) { m_x -= rhs; m_y -= rhs; m_z -= rhs; return *this; }
267
+ inline Float3& operator*=( float const& rhs ) { m_x *= rhs; m_y *= rhs; m_z *= rhs; return *this; }
268
+ inline Float3& operator/=( float const& rhs ) { m_x /= rhs; m_y /= rhs; m_z /= rhs; return *this; }
269
+
270
+ float m_x, m_y, m_z;
271
+ };
272
+
273
+ struct Float4
274
+ {
275
+ static Float4 const Zero;
276
+ static Float4 const One;
277
+ static Float4 const UnitX;
278
+ static Float4 const UnitY;
279
+ static Float4 const UnitZ;
280
+ static Float4 const UnitW;
281
+
282
+ static Float4 const WorldForward;
283
+ static Float4 const WorldUp;
284
+ static Float4 const WorldRight;
285
+
286
+ public:
287
+
288
+ Float4() {}
289
+ FORCE_INLINE Float4( ZeroInit_t ) : m_x( 0 ), m_y( 0 ), m_z( 0 ), m_w( 0 ) {}
290
+ FORCE_INLINE explicit Float4( float v ) : m_x( v ), m_y( v ), m_z( v ), m_w( v ) {}
291
+ FORCE_INLINE explicit Float4( float ix, float iy, float iz, float iw ) : m_x( ix ), m_y( iy ), m_z( iz ), m_w( iw ) {}
292
+ explicit Float4( Float2 const& v, float iz = 0.0f, float iw = 0.0f ) : m_x( v.m_x ), m_y( v.m_y ), m_z( iz ), m_w( iw ) {}
293
+ explicit Float4( Float3 const& v, float iw = 0.0f ) : m_x( v.m_x ), m_y( v.m_y ), m_z( v.m_z ), m_w( iw ) {}
294
+
295
+ inline bool IsZero() const { return *this == Zero; }
296
+
297
+ float& operator[]( uint32_t i ) { return ( (float*) this )[i]; }
298
+ float const& operator[]( uint32_t i ) const { return ( (float*) this )[i]; }
299
+
300
+ FORCE_INLINE Float4 operator-() const { return Float4( -m_x, -m_y, -m_z, -m_w ); }
301
+
302
+ bool operator==( Float4 const rhs ) const { return m_x == rhs.m_x && m_y == rhs.m_y && m_z == rhs.m_z && m_w == rhs.m_w; }
303
+ bool operator!=( Float4 const rhs ) const { return m_x != rhs.m_x || m_y != rhs.m_y || m_z != rhs.m_z || m_w != rhs.m_w; }
304
+
305
+ inline operator Float2() const { return Float2( m_x, m_y ); }
306
+ inline operator Float3() const { return Float3( m_x, m_y, m_z ); }
307
+
308
+ inline Float4 operator+( Float4 const& rhs ) const { return Float4( m_x + rhs.m_x, m_y + rhs.m_y, m_z + rhs.m_z, m_w + rhs.m_w ); }
309
+ inline Float4 operator-( Float4 const& rhs ) const { return Float4( m_x - rhs.m_x, m_y - rhs.m_y, m_z - rhs.m_z, m_w - rhs.m_w ); }
310
+ inline Float4 operator*( Float4 const& rhs ) const { return Float4( m_x * rhs.m_x, m_y * rhs.m_y, m_z * rhs.m_z, m_w * rhs.m_w ); }
311
+ inline Float4 operator/( Float4 const& rhs ) const { return Float4( m_x / rhs.m_x, m_y / rhs.m_y, m_z / rhs.m_z, m_w / rhs.m_w ); }
312
+
313
+ inline Float4 operator+( float const& rhs ) const { return Float4( m_x + rhs, m_y + rhs, m_z + rhs, m_w + rhs ); }
314
+ inline Float4 operator-( float const& rhs ) const { return Float4( m_x - rhs, m_y - rhs, m_z - rhs, m_w - rhs ); }
315
+ inline Float4 operator*( float const& rhs ) const { return Float4( m_x * rhs, m_y * rhs, m_z * rhs, m_w * rhs ); }
316
+ inline Float4 operator/( float const& rhs ) const { return Float4( m_x / rhs, m_y / rhs, m_z / rhs, m_w / rhs ); }
317
+
318
+ inline Float4& operator+=( Float4 const& rhs ) { m_x += rhs.m_x; m_y += rhs.m_y; m_z += rhs.m_z; m_w += rhs.m_w; return *this; }
319
+ inline Float4& operator-=( Float4 const& rhs ) { m_x -= rhs.m_x; m_y -= rhs.m_y; m_z -= rhs.m_z; m_w -= rhs.m_w; return *this; }
320
+ inline Float4& operator*=( Float4 const& rhs ) { m_x *= rhs.m_x; m_y *= rhs.m_y; m_z *= rhs.m_z; m_w *= rhs.m_w; return *this; }
321
+ inline Float4& operator/=( Float4 const& rhs ) { m_x /= rhs.m_x; m_y /= rhs.m_y; m_z /= rhs.m_z; m_w /= rhs.m_w; return *this; }
322
+
323
+ inline Float4& operator+=( float const& rhs ) { m_x += rhs; m_y += rhs; m_z += rhs; m_w += rhs; return *this; }
324
+ inline Float4& operator-=( float const& rhs ) { m_x -= rhs; m_y -= rhs; m_z -= rhs; m_w -= rhs; return *this; }
325
+ inline Float4& operator*=( float const& rhs ) { m_x *= rhs; m_y *= rhs; m_z *= rhs; m_w *= rhs; return *this; }
326
+ inline Float4& operator/=( float const& rhs ) { m_x /= rhs; m_y /= rhs; m_z /= rhs; m_w /= rhs; return *this; }
327
+
328
+ float m_x, m_y, m_z, m_w;
329
+ };
330
+
331
+ inline Int2::Int2( Float2 const& v )
332
+ : m_x( (int32_t) v.m_x )
333
+ , m_y( (int32_t) v.m_y )
334
+ {
335
+ }
336
+
337
+ inline Int3::Int3( Float3 const& v )
338
+ : m_x( (int32_t) v.m_x )
339
+ , m_y( (int32_t) v.m_y )
340
+ , m_z( (int32_t) v.m_z )
341
+ {
342
+ }
343
+
344
+ inline Float2::Float2( Float3 const& v )
345
+ : m_x( v.m_x )
346
+ , m_y( v.m_y )
347
+ {
348
+ }
349
+
350
+ inline Float2::Float2( Float4 const& v )
351
+ : m_x( v.m_x )
352
+ , m_y( v.m_y )
353
+ {
354
+ }
355
+
356
+ inline Float3::Float3( Float4 const& v )
357
+ : m_x( v.m_x )
358
+ , m_y( v.m_y )
359
+ , m_z( v.m_z )
360
+ {
361
+ }
362
+
363
+ struct Radians;
364
+ struct Degrees;
365
+
366
+ struct Degrees
367
+ {
368
+ public:
369
+
370
+ inline Degrees() = default;
371
+ inline Degrees( float degrees ) : m_value( degrees ) {}
372
+ inline explicit Degrees( Radians const& radians );
373
+
374
+ FORCE_INLINE explicit operator float() const { return m_value; }
375
+ FORCE_INLINE operator Radians() const;
376
+ FORCE_INLINE float ToFloat() const { return m_value; }
377
+ FORCE_INLINE Radians ToRadians() const;
378
+
379
+ inline Degrees operator-() const { return Degrees( -m_value ); }
380
+
381
+ inline Degrees operator+( Degrees const& rhs ) const { return Degrees( m_value + rhs.m_value ); }
382
+ inline Degrees operator-( Degrees const& rhs ) const { return Degrees( m_value - rhs.m_value ); }
383
+ inline Degrees operator*( Degrees const& rhs ) const { return Degrees( m_value * rhs.m_value ); }
384
+ inline Degrees operator/( Degrees const& rhs ) const { return Degrees( m_value / rhs.m_value ); }
385
+
386
+ inline Degrees& operator+=( Degrees const& rhs ) { m_value += rhs.m_value; return *this; }
387
+ inline Degrees& operator-=( Degrees const& rhs ) { m_value -= rhs.m_value; return *this; }
388
+ inline Degrees& operator*=( Degrees const& rhs ) { m_value *= rhs.m_value; return *this; }
389
+ inline Degrees& operator/=( Degrees const& rhs ) { m_value /= rhs.m_value; return *this; }
390
+
391
+ inline Degrees operator+( float const& rhs ) const { return Degrees( m_value + rhs ); }
392
+ inline Degrees operator-( float const& rhs ) const { return Degrees( m_value - rhs ); }
393
+ inline Degrees operator*( float const& rhs ) const { return Degrees( m_value * rhs ); }
394
+ inline Degrees operator/( float const& rhs ) const { return Degrees( m_value / rhs ); }
395
+
396
+ inline Degrees& operator+=( float const& rhs ) { m_value += rhs; return *this; }
397
+ inline Degrees& operator-=( float const& rhs ) { m_value -= rhs; return *this; }
398
+ inline Degrees& operator*=( float const& rhs ) { m_value *= rhs; return *this; }
399
+ inline Degrees& operator/=( float const& rhs ) { m_value /= rhs; return *this; }
400
+
401
+ inline Degrees operator+( int32_t const& rhs ) const { return Degrees( m_value + rhs ); }
402
+ inline Degrees operator-( int32_t const& rhs ) const { return Degrees( m_value - rhs ); }
403
+ inline Degrees operator*( int32_t const& rhs ) const { return Degrees( m_value * rhs ); }
404
+ inline Degrees operator/( int32_t const& rhs ) const { return Degrees( m_value / rhs ); }
405
+
406
+ inline Degrees& operator+=( int32_t const& rhs ) { m_value += rhs; return *this; }
407
+ inline Degrees& operator-=( int32_t const& rhs ) { m_value -= rhs; return *this; }
408
+ inline Degrees& operator*=( int32_t const& rhs ) { m_value *= rhs; return *this; }
409
+ inline Degrees& operator/=( int32_t const& rhs ) { m_value /= rhs; return *this; }
410
+
411
+ inline Degrees operator+( uint32_t const& rhs ) const { return Degrees( m_value + rhs ); }
412
+ inline Degrees operator-( uint32_t const& rhs ) const { return Degrees( m_value - rhs ); }
413
+ inline Degrees operator*( uint32_t const& rhs ) const { return Degrees( m_value * rhs ); }
414
+ inline Degrees operator/( uint32_t const& rhs ) const { return Degrees( m_value / rhs ); }
415
+
416
+ inline Degrees& operator+=( uint32_t const& rhs ) { m_value += rhs; return *this; }
417
+ inline Degrees& operator-=( uint32_t const& rhs ) { m_value -= rhs; return *this; }
418
+ inline Degrees& operator*=( uint32_t const& rhs ) { m_value *= rhs; return *this; }
419
+ inline Degrees& operator/=( uint32_t const& rhs ) { m_value /= rhs; return *this; }
420
+
421
+ inline bool operator>( float const& rhs ) const { return m_value > rhs; };
422
+ inline bool operator<( float const& rhs ) const { return m_value < rhs; }
423
+ inline bool operator>=( float const& rhs ) const { return m_value >= rhs; }
424
+ inline bool operator<=( float const& rhs ) const { return m_value <= rhs; }
425
+
426
+ inline bool operator>( Degrees const& rhs ) const { return m_value > rhs.m_value; }
427
+ inline bool operator<( Degrees const& rhs ) const { return m_value < rhs.m_value; }
428
+ inline bool operator>=( Degrees const& rhs ) const { return m_value >= rhs.m_value; }
429
+ inline bool operator<=( Degrees const& rhs ) const { return m_value <= rhs.m_value; }
430
+
431
+ inline bool operator>( Radians const& rhs ) const;
432
+ inline bool operator<( Radians const& rhs ) const;
433
+ inline bool operator>=( Radians const& rhs ) const;
434
+ inline bool operator<=( Radians const& rhs ) const;
435
+
436
+ inline bool operator==( float const& v ) const { return Math::IsNearEqual( m_value, v ); }
437
+ inline bool operator!=( float const& v ) const { return !Math::IsNearEqual( m_value, v ); }
438
+
439
+ inline bool operator==( Degrees const& rhs ) const { return m_value == rhs.m_value; }
440
+ inline bool operator!=( Degrees const& rhs ) const { return m_value != rhs.m_value; }
441
+
442
+ inline bool operator==( Radians const& rhs ) const;
443
+ inline bool operator!=( Radians const& rhs ) const;
444
+
445
+ inline void Clamp( Degrees min, Degrees max )
446
+ {
447
+ m_value = Math::Clamp( m_value, min.m_value, max.m_value );
448
+ }
449
+
450
+ // Clamps between -360 and 360
451
+ inline void Clamp360()
452
+ {
453
+ m_value -= ( int32_t( m_value / 360.0f ) * 360.0f );
454
+ }
455
+
456
+ // Clamps between -360 and 360
457
+ inline Degrees GetClamped360() const
458
+ {
459
+ Degrees d( m_value );
460
+ d.Clamp360();
461
+ return d;
462
+ }
463
+
464
+ // Clamps to -180 to 180
465
+ inline void Clamp180()
466
+ {
467
+ Clamp360();
468
+
469
+ float delta = 180 - Math::Abs( m_value );
470
+ if ( delta < 0 )
471
+ {
472
+ delta += 180;
473
+ m_value = ( m_value < 0 ) ? delta : -delta;
474
+ }
475
+ }
476
+
477
+ // Clamps to -180 to 180
478
+ inline Degrees GetClamped180() const
479
+ {
480
+ Degrees r( m_value );
481
+ r.Clamp180();
482
+ return r;
483
+ }
484
+
485
+ // Clamps between 0 to 360
486
+ inline Degrees& ClampPositive360()
487
+ {
488
+ Clamp360();
489
+ if ( m_value < 0 )
490
+ {
491
+ m_value += 360;
492
+ }
493
+ return *this;
494
+ }
495
+
496
+ // Clamps between 0 to 360
497
+ inline Degrees GetClampedPositive360() const
498
+ {
499
+ Degrees d( m_value );
500
+ d.ClampPositive360();
501
+ return d;
502
+ }
503
+
504
+ private:
505
+
506
+ float m_value = 0;
507
+ };
508
+
509
+ struct Radians
510
+ {
511
+ static Radians const Pi;
512
+ static Radians const TwoPi;
513
+ static Radians const OneDivPi;
514
+ static Radians const OneDivTwoPi;
515
+ static Radians const PiDivTwo;
516
+ static Radians const PiDivFour;
517
+
518
+ public:
519
+
520
+ inline Radians() = default;
521
+ inline Radians( float radians ) : m_value( radians ) {}
522
+ inline explicit Radians( Degrees const& degrees );
523
+
524
+ FORCE_INLINE explicit operator float() const { return m_value; }
525
+ FORCE_INLINE operator Degrees() const { return ToDegrees(); }
526
+ FORCE_INLINE float ToFloat() const { return m_value; }
527
+ FORCE_INLINE Degrees ToDegrees() const { return Degrees( m_value * Math::RadiansToDegrees ); }
528
+
529
+ inline Radians operator-() const { return Radians( -m_value ); }
530
+
531
+ inline Radians operator+( Radians const& rhs ) const { return Radians( m_value + rhs.m_value ); }
532
+ inline Radians operator-( Radians const& rhs ) const { return Radians( m_value - rhs.m_value ); }
533
+ inline Radians operator*( Radians const& rhs ) const { return Radians( m_value * rhs.m_value ); }
534
+ inline Radians operator/( Radians const& rhs ) const { return Radians( m_value / rhs.m_value ); }
535
+
536
+ inline Radians& operator+=( Radians const& rhs ) { m_value += rhs.m_value; return *this; }
537
+ inline Radians& operator-=( Radians const& rhs ) { m_value -= rhs.m_value; return *this; }
538
+ inline Radians& operator*=( Radians const& rhs ) { m_value *= rhs.m_value; return *this; }
539
+ inline Radians& operator/=( Radians const& rhs ) { m_value /= rhs.m_value; return *this; }
540
+
541
+ inline Radians operator+( float const& rhs ) const { return Radians( m_value + rhs ); }
542
+ inline Radians operator-( float const& rhs ) const { return Radians( m_value - rhs ); }
543
+ inline Radians operator*( float const& rhs ) const { return Radians( m_value * rhs ); }
544
+ inline Radians operator/( float const& rhs ) const { return Radians( m_value / rhs ); }
545
+
546
+ inline Radians& operator+=( float const& rhs ) { m_value += rhs; return *this; }
547
+ inline Radians& operator-=( float const& rhs ) { m_value -= rhs; return *this; }
548
+ inline Radians& operator*=( float const& rhs ) { m_value *= rhs; return *this; }
549
+ inline Radians& operator/=( float const& rhs ) { m_value /= rhs; return *this; }
550
+
551
+ inline Radians operator+( int32_t const& rhs ) const { return Radians( m_value + rhs ); }
552
+ inline Radians operator-( int32_t const& rhs ) const { return Radians( m_value - rhs ); }
553
+ inline Radians operator*( int32_t const& rhs ) const { return Radians( m_value * rhs ); }
554
+ inline Radians operator/( int32_t const& rhs ) const { return Radians( m_value / rhs ); }
555
+
556
+ inline Radians& operator+=( int32_t const& rhs ) { m_value += rhs; return *this; }
557
+ inline Radians& operator-=( int32_t const& rhs ) { m_value -= rhs; return *this; }
558
+ inline Radians& operator*=( int32_t const& rhs ) { m_value *= rhs; return *this; }
559
+ inline Radians& operator/=( int32_t const& rhs ) { m_value /= rhs; return *this; }
560
+
561
+ inline Radians operator+( uint32_t const& rhs ) const { return Radians( m_value + rhs ); }
562
+ inline Radians operator-( uint32_t const& rhs ) const { return Radians( m_value - rhs ); }
563
+ inline Radians operator*( uint32_t const& rhs ) const { return Radians( m_value * rhs ); }
564
+ inline Radians operator/( uint32_t const& rhs ) const { return Radians( m_value / rhs ); }
565
+
566
+ inline Radians& operator+=( uint32_t const& rhs ) { m_value += rhs; return *this; }
567
+ inline Radians& operator-=( uint32_t const& rhs ) { m_value -= rhs; return *this; }
568
+ inline Radians& operator*=( uint32_t const& rhs ) { m_value *= rhs; return *this; }
569
+ inline Radians& operator/=( uint32_t const& rhs ) { m_value /= rhs; return *this; }
570
+
571
+ inline bool operator>( float const& rhs ) const { return m_value > rhs; };
572
+ inline bool operator<( float const& rhs ) const { return m_value < rhs; }
573
+ inline bool operator>=( float const& rhs ) const { return m_value >= rhs; }
574
+ inline bool operator<=( float const& rhs ) const { return m_value <= rhs; }
575
+
576
+ inline bool operator>( Radians const& rhs ) const { return m_value > rhs.m_value; }
577
+ inline bool operator<( Radians const& rhs ) const { return m_value < rhs.m_value; }
578
+ inline bool operator>=( Radians const& rhs ) const { return m_value >= rhs.m_value; }
579
+ inline bool operator<=( Radians const& rhs ) const { return m_value <= rhs.m_value; }
580
+
581
+ inline bool operator>( Degrees const& rhs ) const;
582
+ inline bool operator<( Degrees const& rhs ) const;
583
+ inline bool operator>=( Degrees const& rhs ) const;
584
+ inline bool operator<=( Degrees const& rhs ) const;
585
+
586
+ inline bool operator==( float const& v ) const { return Math::IsNearEqual( m_value, v ); }
587
+ inline bool operator!=( float const& v ) const { return !Math::IsNearEqual( m_value, v ); }
588
+
589
+ inline bool operator==( Radians const& rhs ) const { return m_value == rhs.m_value; }
590
+ inline bool operator!=( Radians const& rhs ) const { return m_value != rhs.m_value; }
591
+
592
+ inline bool operator==( Degrees const& rhs ) const;
593
+ inline bool operator!=( Degrees const& rhs ) const;
594
+
595
+ inline void Clamp( Radians min, Radians max )
596
+ {
597
+ m_value = Math::Clamp( m_value, min.m_value, max.m_value );
598
+ }
599
+
600
+ // Clamps between -2Pi to 2Pi
601
+ inline void Clamp360()
602
+ {
603
+ m_value -= int32_t( m_value / Math::TwoPi ) * Math::TwoPi;
604
+ }
605
+
606
+ // Clamps between -2Pi to 2Pi
607
+ inline Radians GetClamped360() const
608
+ {
609
+ Radians r( m_value );
610
+ r.Clamp360();
611
+ return r;
612
+ }
613
+
614
+ // Clamps between 0 to 2Pi
615
+ inline void ClampPositive360()
616
+ {
617
+ Clamp360();
618
+ if( m_value < 0 )
619
+ {
620
+ m_value += Math::TwoPi;
621
+ }
622
+ }
623
+
624
+ // Clamps between 0 to 2Pi
625
+ inline Radians GetClampedToPositive360() const
626
+ {
627
+ Radians r( m_value );
628
+ r.ClampPositive360();
629
+ return r;
630
+ }
631
+
632
+ // Clamps to -Pi to Pi
633
+ inline void Clamp180()
634
+ {
635
+ Clamp360();
636
+
637
+ float delta = Math::Pi - Math::Abs( m_value );
638
+ if ( delta < 0 )
639
+ {
640
+ delta += Math::Pi;
641
+ m_value = ( m_value < 0 ) ? delta : -delta;
642
+ }
643
+ }
644
+
645
+ // Clamps to -Pi to Pi
646
+ inline Radians GetClamped180() const
647
+ {
648
+ Radians r( m_value );
649
+ r.Clamp180();
650
+ return r;
651
+ }
652
+
653
+ // Inverts angle between [0;2Pi] and [-2Pi;0]
654
+ inline void Invert()
655
+ {
656
+ Clamp360();
657
+ float const delta = Math::TwoPi - Math::Abs( m_value );
658
+ m_value = ( m_value < 0 ) ? delta : -delta;
659
+ }
660
+
661
+ // Inverts angle between [0;2Pi] and [-2Pi;0]
662
+ inline Radians GetInverse() const
663
+ {
664
+ Radians r( m_value );
665
+ r.Invert();
666
+ return r;
667
+ }
668
+
669
+ // Flips the front and rear 180 degree arc i.e. 135 becomes -45, -90 becomes 90, etc.
670
+ inline void Flip()
671
+ {
672
+ Clamp180();
673
+ float const delta = Math::Pi - Math::Abs( m_value );
674
+ m_value = ( m_value < 0 ) ? delta : -delta;
675
+ }
676
+
677
+ // Flips the front and rear 180 degree arc i.e. 135 becomes -45, -90 becomes 90, etc.
678
+ inline Radians GetFlipped() const
679
+ {
680
+ Radians r( m_value );
681
+ r.Flip();
682
+ return r;
683
+ }
684
+
685
+ private:
686
+
687
+ float m_value = 0;
688
+ };
689
+
690
+ inline Degrees::Degrees( Radians const& radians )
691
+ : m_value( radians.ToDegrees() )
692
+ {}
693
+
694
+ inline Radians Degrees::ToRadians() const
695
+ {
696
+ return Radians( m_value * Math::DegreesToRadians );
697
+ }
698
+
699
+ inline Degrees::operator Radians() const
700
+ {
701
+ return ToRadians();
702
+ }
703
+
704
+ inline bool Degrees::operator>( Radians const& rhs ) const { return m_value > rhs.ToDegrees().m_value; }
705
+ inline bool Degrees::operator<( Radians const& rhs ) const { return m_value < rhs.ToDegrees().m_value; }
706
+ inline bool Degrees::operator>=( Radians const& rhs ) const { return m_value >= rhs.ToDegrees().m_value; }
707
+ inline bool Degrees::operator<=( Radians const& rhs ) const { return m_value <= rhs.ToDegrees().m_value; }
708
+
709
+ inline bool Degrees::operator==( Radians const& rhs ) const { return Math::IsNearEqual( m_value, rhs.ToDegrees().m_value ); }
710
+ inline bool Degrees::operator!=( Radians const& rhs ) const { return !Math::IsNearEqual( m_value, rhs.ToDegrees().m_value ); }
711
+
712
+ inline Radians::Radians( Degrees const& degrees )
713
+ : m_value( degrees.ToRadians() )
714
+ {}
715
+
716
+ inline bool Radians::operator>( Degrees const& rhs ) const { return m_value > rhs.ToRadians().m_value; }
717
+ inline bool Radians::operator<( Degrees const& rhs ) const { return m_value < rhs.ToRadians().m_value; }
718
+ inline bool Radians::operator>=( Degrees const& rhs ) const { return m_value >= rhs.ToRadians().m_value; }
719
+ inline bool Radians::operator<=( Degrees const& rhs ) const { return m_value <= rhs.ToRadians().m_value; }
720
+
721
+ inline bool Radians::operator==( Degrees const& rhs ) const { return Math::IsNearEqual( m_value, rhs.ToRadians().m_value ); }
722
+ inline bool Radians::operator!=( Degrees const& rhs ) const { return !Math::IsNearEqual( m_value, rhs.ToRadians().m_value ); }
723
+
724
+ struct EulerAngles
725
+ {
726
+ public:
727
+
728
+ EulerAngles() = default;
729
+
730
+ inline explicit EulerAngles( Degrees inX, Degrees inY, Degrees inZ )
731
+ : m_x( inX )
732
+ , m_y( inY )
733
+ , m_z( inZ )
734
+ {}
735
+
736
+ inline explicit EulerAngles( Radians inX, Radians inY, Radians inZ )
737
+ : m_x( inX )
738
+ , m_y( inY )
739
+ , m_z( inZ )
740
+ {}
741
+
742
+ inline explicit EulerAngles( float inDegreesX, float inDegreesY, float inDegreesZ )
743
+ : m_x( Math::DegreesToRadians * inDegreesX )
744
+ , m_y( Math::DegreesToRadians * inDegreesY )
745
+ , m_z( Math::DegreesToRadians * inDegreesZ )
746
+ {}
747
+
748
+ inline EulerAngles( Float3 const& anglesInDegrees )
749
+ : m_x( Math::DegreesToRadians * anglesInDegrees.m_x )
750
+ , m_y( Math::DegreesToRadians * anglesInDegrees.m_y )
751
+ , m_z( Math::DegreesToRadians * anglesInDegrees.m_z )
752
+ {}
753
+
754
+ inline void Clamp()
755
+ {
756
+ m_x.Clamp360();
757
+ m_y.Clamp360();
758
+ m_z.Clamp360();
759
+ }
760
+
761
+ inline EulerAngles GetClamped() const
762
+ {
763
+ EulerAngles clamped = *this;
764
+ clamped.Clamp();
765
+ return clamped;
766
+ }
767
+
768
+ inline Radians GetYaw() const { return m_z; }
769
+ inline Radians GetPitch() const { return m_x; }
770
+ inline Radians GetRoll() const { return m_y; }
771
+
772
+ inline Float3 GetAsRadians() const { return Float3( m_x.ToFloat(), m_y.ToFloat(), m_z.ToFloat() ); }
773
+ inline Float3 GetAsDegrees() const { return Float3( m_x.ToDegrees().ToFloat(), m_y.ToDegrees().ToFloat(), m_z.ToDegrees().ToFloat() ); }
774
+
775
+ inline bool operator==( EulerAngles const& other ) const { return m_x == other.m_x && m_y == other.m_y && m_z == other.m_z; }
776
+ inline bool operator!=( EulerAngles const& other ) const { return m_x != other.m_x || m_y != other.m_y || m_z != other.m_z; }
777
+
778
+ inline Radians& operator[]( uint32_t i ) { return ( (Radians*) this )[i]; }
779
+ inline Radians const& operator[]( uint32_t i ) const { return ( (Radians*) this )[i]; }
780
+ // in degrees
781
+ inline Float3 ToFloat3() const { return Float3( Math::RadiansToDegrees * m_x.ToFloat(), Math::RadiansToDegrees * m_y.ToFloat(), Math::RadiansToDegrees * m_z.ToFloat() ); }
782
+
783
+ public:
784
+
785
+ Radians m_x = 0.0f;
786
+ Radians m_y = 0.0f;
787
+ Radians m_z = 0.0f;
788
+ };
789
+
790
+ struct AxisAngle
791
+ {
792
+ public:
793
+
794
+ inline AxisAngle() = default;
795
+ inline explicit AxisAngle( Float3 axis, Radians angle ) : m_axis( axis ), m_angle( angle ) {}
796
+ inline explicit AxisAngle( Float3 axis, Degrees angle ) : m_axis( axis ), m_angle( angle.ToRadians() ) {}
797
+
798
+ inline bool IsValid() const
799
+ {
800
+ float const lengthSq = m_axis.m_x * m_axis.m_x + m_axis.m_y * m_axis.m_y + m_axis.m_z * m_axis.m_z;
801
+ return Math::Abs( lengthSq - 1.0f ) < Math::Epsilon;
802
+ }
803
+
804
+ public:
805
+
806
+ Float3 m_axis = Float3::Zero;
807
+ Radians m_angle = Radians( 0.0f );
808
+ };
MotionCorrection/src/cpp/Math/Vector.cpp ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #include "Vector.h"
7
+ #include "Quaternion.h"
8
+
9
+ namespace Math
10
+ {
11
+ Vector const Vector::UnitX = { 1, 0, 0, 0 };
12
+ Vector const Vector::UnitY = { 0, 1, 0, 0 };
13
+ Vector const Vector::UnitZ = { 0, 0, 1, 0 };
14
+ Vector const Vector::UnitW = { 0, 0, 0, 1 };
15
+
16
+ Vector const Vector::Origin = { 0, 0, 0, 1 };
17
+ Vector const Vector::WorldForward = { 0, -1, 0, 0 };
18
+ Vector const Vector::WorldBackward = { 0, 1, 0, 0 };
19
+ Vector const Vector::WorldUp = { 0, 0, 1, 0 };
20
+ Vector const Vector::WorldDown = { 0, 0, -1, 0 };
21
+ Vector const Vector::WorldLeft = { 1, 0, 0, 0 };
22
+ Vector const Vector::WorldRight = { -1, 0, 0, 0 };
23
+
24
+ Vector const Vector::Infinity = { 0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000 };
25
+ Vector const Vector::QNaN = { 0x7FC00000, 0x7FC00000, 0x7FC00000, 0x7FC00000 };
26
+
27
+ Vector const Vector::NegativeOne(-1.0f);
28
+ Vector const Vector::Zero(0.0f);
29
+ Vector const Vector::Half(0.5f);
30
+ Vector const Vector::One(1.0f);
31
+
32
+ Vector const Vector::Epsilon(Math::Epsilon);
33
+ Vector const Vector::LargeEpsilon(Math::LargeEpsilon);
34
+ Vector const Vector::OneMinusEpsilon(1.0f - Math::Epsilon);
35
+ Vector const Vector::EpsilonMinusOne(Math::Epsilon - 1.0f);
36
+ Vector const Vector::NormalizeCheckThreshold(0.01f); // Squared Error
37
+
38
+ Vector const Vector::Pi(Math::Pi);
39
+ Vector const Vector::PiDivTwo(Math::PiDivTwo);
40
+ Vector const Vector::TwoPi(Math::TwoPi);
41
+ Vector const Vector::OneDivTwoPi(Math::OneDivTwoPi);
42
+
43
+ Vector const Vector::Select0000(0, 0, 0, 0);
44
+ Vector const Vector::Select0001(0, 0, 0, 1);
45
+ Vector const Vector::Select0010(0, 0, 1, 0);
46
+ Vector const Vector::Select0011(0, 0, 1, 1);
47
+ Vector const Vector::Select0100(0, 1, 0, 0);
48
+ Vector const Vector::Select0101(0, 1, 0, 1);
49
+ Vector const Vector::Select0110(0, 1, 1, 0);
50
+ Vector const Vector::Select0111(0, 1, 1, 1);
51
+ Vector const Vector::Select1000(1, 0, 0, 0);
52
+ Vector const Vector::Select1001(1, 0, 0, 1);
53
+ Vector const Vector::Select1010(1, 0, 1, 0);
54
+ Vector const Vector::Select1011(1, 0, 1, 1);
55
+ Vector const Vector::Select1100(1, 1, 0, 0);
56
+ Vector const Vector::Select1101(1, 1, 0, 1);
57
+ Vector const Vector::Select1110(1, 1, 1, 0);
58
+ Vector const Vector::Select1111(1, 1, 1, 1);
59
+
60
+ Vector const Vector::BoxCorners[8] =
61
+ {
62
+ { -1.0f, -1.0f, 1.0f, 0.0f },
63
+ { 1.0f, -1.0f, 1.0f, 0.0f },
64
+ { 1.0f, 1.0f, 1.0f, 0.0f },
65
+ { -1.0f, 1.0f, 1.0f, 0.0f },
66
+ { -1.0f, -1.0f, -1.0f, 0.0f },
67
+ { 1.0f, -1.0f, -1.0f, 0.0f },
68
+ { 1.0f, 1.0f, -1.0f, 0.0f },
69
+ { -1.0f, 1.0f, -1.0f, 0.0f },
70
+ };
71
+
72
+ Vector Vector::SLerp(const Vector& from, const Vector& to, float t)
73
+ {
74
+ ASSERT(t >= 0.0f && t <= 1.0f);
75
+ if (from.LengthSquared3().IsLessThan4(Epsilon) || to.LengthSquared3().IsLessThan4(Epsilon))
76
+ {
77
+ return Lerp(from, to, t);
78
+ }
79
+
80
+ // Calculate the final length
81
+ const Vector fromLength = from.Length3();
82
+ const Vector toLength = to.Length3();
83
+ const Vector finalLength = Lerp(fromLength, toLength, t);
84
+
85
+ // Normalize vectors
86
+ const Vector normalizedFrom = from / fromLength;
87
+ const Vector normalizedTo = to / toLength;
88
+
89
+ // Handle parallel vector
90
+ Vector result;
91
+ if (normalizedFrom.IsParallelTo(normalizedTo))
92
+ {
93
+ result = normalizedFrom;
94
+ }
95
+ else
96
+ {
97
+ // Interpolate the rotation between the vectors
98
+ const Vector dot = Dot3(normalizedFrom, normalizedTo);
99
+ const Vector angle = ACos(dot);
100
+ const Vector axis = Cross3(normalizedFrom, normalizedTo).Normalize3();
101
+ const Vector interpolatedAngle = Lerp(Zero, angle, t);
102
+
103
+ const Quaternion rotation(axis, Radians(interpolatedAngle.ToFloat()));
104
+ const Vector finalDirection = rotation.RotateVector(normalizedFrom);
105
+ result = finalDirection.GetNormalized3() * finalLength;
106
+ }
107
+
108
+ return result;
109
+ }
110
+ }
MotionCorrection/src/cpp/Math/Vector.h ADDED
@@ -0,0 +1,454 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include "Compiler.h"
9
+
10
+ #include "Types.h"
11
+ #include "Constants.h"
12
+ #include "SIMD.h"
13
+
14
+ namespace Math
15
+ {
16
+ class alignas(16) Vector
17
+ {
18
+ public:
19
+
20
+ static Vector const UnitX;
21
+ static Vector const UnitY;
22
+ static Vector const UnitZ;
23
+ static Vector const UnitW;
24
+
25
+ static Vector const Origin;
26
+ static Vector const WorldForward;
27
+ static Vector const WorldBackward;
28
+ static Vector const WorldUp;
29
+ static Vector const WorldDown;
30
+ static Vector const WorldLeft;
31
+ static Vector const WorldRight;
32
+
33
+ static Vector const NegativeOne;
34
+ static Vector const Zero;
35
+ static Vector const Half;
36
+ static Vector const One;
37
+ static Vector const Epsilon;
38
+ static Vector const LargeEpsilon;
39
+ static Vector const OneMinusEpsilon;
40
+ static Vector const EpsilonMinusOne;
41
+ static Vector const NormalizeCheckThreshold;
42
+ static Vector const Pi;
43
+ static Vector const PiDivTwo;
44
+ static Vector const TwoPi;
45
+ static Vector const OneDivTwoPi;
46
+
47
+ static Vector const Select0000;
48
+ static Vector const Select0001;
49
+ static Vector const Select0010;
50
+ static Vector const Select0011;
51
+ static Vector const Select0100;
52
+ static Vector const Select0101;
53
+ static Vector const Select0110;
54
+ static Vector const Select0111;
55
+ static Vector const Select1000;
56
+ static Vector const Select1001;
57
+ static Vector const Select1010;
58
+ static Vector const Select1011;
59
+ static Vector const Select1100;
60
+ static Vector const Select1101;
61
+ static Vector const Select1110;
62
+ static Vector const Select1111;
63
+
64
+ static Vector const Infinity;
65
+ static Vector const QNaN;
66
+
67
+ static Vector const BoxCorners[8];
68
+
69
+ //
70
+ // Utils
71
+ //
72
+
73
+ static Vector Cross2(const Vector& v0, const Vector& v1);
74
+ static Vector Cross3(const Vector& v0, const Vector& v1);
75
+ static Vector Dot2(const Vector& v0, const Vector& v1);
76
+ static Vector Dot3(const Vector& v0, const Vector& v1);
77
+ static Vector Dot4(const Vector& v0, const Vector& v1);
78
+ static Vector Average2(const Vector& v0, const Vector& v1);
79
+ static Vector Average3(const Vector& v0, const Vector& v1);
80
+ static Vector Average4(const Vector& v0, const Vector& v1);
81
+ static Vector Min(const Vector& v0, const Vector& v1);
82
+ static Vector Max(const Vector& v0, const Vector& v1);
83
+ static float Min(const Vector& v);
84
+ static float Max(const Vector& v);
85
+ static Vector Clamp(const Vector& v, const Vector& min, const Vector& max);
86
+ static Vector Xor(const Vector& vec0, const Vector& vec1);
87
+
88
+ // Add the multiplied results to a vector: ( vec * mul ) + addend
89
+ static Vector MultiplyAdd(const Vector& vec, const Vector& multiplier, const Vector& addend);
90
+
91
+ // Subtract a vector from the multiplied result: (vec * mul ) - subtrahend
92
+ static Vector MultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& subtrahend);
93
+
94
+ // Subtract the multiplied result from a vector: minuend - (vec * mul )
95
+ static Vector NegativeMultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& minuend);
96
+
97
+ // Sum up scaled versions of two vectors
98
+ static Vector LinearCombination(const Vector& v0, const Vector& v1, float scale0, float scale1);
99
+
100
+ // Linear interpolation of one vector to another
101
+ static Vector Lerp(const Vector& from, const Vector& to, float t);
102
+
103
+ // Normalized linear interpolation of one vector to another
104
+ static Vector NLerp(const Vector& from, const Vector& to, float t);
105
+
106
+ // Spherical interpolation of one vector to another
107
+ static Vector SLerp(const Vector& from, const Vector& to, float t);
108
+
109
+ // Combine the two vectors based on the control: 0 means select from v0, 1 means select from v1. E.G. To select XY from v0 and ZW from v1, control = Vector( 0, 0, 1, 1 )
110
+ static Vector Select(const Vector& v0, const Vector& v1, const Vector& control);
111
+
112
+ // Get a permutation of two vectors, each template argument represents the element index to select ( v0: 0-3, v1: 4-7 );
113
+ template<uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW>
114
+ static Vector Permute(const Vector& v0, const Vector& v1);
115
+
116
+ //
117
+ // Trigonometry
118
+ //
119
+
120
+ static Vector Sin(const Vector& vec);
121
+ static Vector Cos(const Vector& vec);
122
+ static Vector Tan(const Vector& vec);
123
+ static Vector ASin(const Vector& vec);
124
+ static Vector ACos(const Vector& vec);
125
+ static Vector ATan(const Vector& vec);
126
+ static Vector ATan2(const Vector& vec0, const Vector& vec1);
127
+
128
+ static Vector SinEst(const Vector& vec);
129
+ static Vector CosEst(const Vector& vec);
130
+ static Vector TanEst(const Vector& vec);
131
+ static Vector ASinEst(const Vector& vec);
132
+ static Vector ACosEst(const Vector& vec);
133
+ static Vector ATanEst(const Vector& vec);
134
+ static Vector ATan2Est(const Vector& vec0, const Vector& vec1);
135
+
136
+ static void SinCos(Vector& sin, Vector& cos, float angle);
137
+ static void SinCos(Vector& sin, Vector& cos, const Vector& angle);
138
+
139
+ static Vector AngleMod2Pi(const Vector& angles);
140
+
141
+ public:
142
+
143
+ operator __m128& ();
144
+ operator const __m128& () const;
145
+
146
+ Vector();
147
+ explicit Vector(Axis axis);
148
+ explicit Vector(ZeroInit_t);
149
+ explicit Vector(float v);
150
+ Vector(__m128 v);
151
+ Vector(float ix, float iy, float iz, float iw = 1.0f);
152
+
153
+ Vector(const Float2& v, float iz = 0.0f, float iw = 0.0f);
154
+ Vector(const Float3& v, float iw = 1.0f);
155
+ Vector(const Float4& v);
156
+ Vector(const float* pValues);
157
+
158
+ bool IsValid() const;
159
+
160
+ void Store(float* pValues) const;
161
+ void StoreFloat(float& value) const;
162
+ void StoreFloat2(Float2& value) const;
163
+ void StoreFloat3(Float3& value) const;
164
+ void StoreFloat4(Float4& value) const;
165
+
166
+ float ToFloat() const;
167
+ Float2 ToFloat2() const;
168
+ Float3 ToFloat3() const;
169
+ Float4 ToFloat4() const;
170
+
171
+ operator Float2() const;
172
+ operator Float3() const;
173
+ operator Float4() const;
174
+
175
+ //
176
+ // Element accessors
177
+ //
178
+
179
+ float GetX() const;
180
+ float GetY() const;
181
+ float GetZ() const;
182
+ float GetW() const;
183
+
184
+ void SetX(float x);
185
+ void SetY(float y);
186
+ void SetZ(float z);
187
+ void SetW(float w);
188
+
189
+ float operator[](uint32_t i) const;
190
+
191
+ //
192
+ // W component operations
193
+ //
194
+
195
+ bool IsW1() const;
196
+ bool IsW0() const;
197
+ Vector& SetW0();
198
+ Vector& SetW1();
199
+ Vector GetWithW0() const;
200
+ Vector GetWithW1() const;
201
+
202
+ //
203
+ // Dimensional Getters
204
+ //
205
+
206
+ // Returns only the first two components, z=w=0
207
+ Vector Get2D() const;
208
+
209
+ // Returns only the first three components, w = 0
210
+ Vector Get3D() const;
211
+
212
+ //
213
+ // Algebraic operators
214
+ //
215
+
216
+ Vector operator+(const Vector& v) const;
217
+ Vector& operator+=(const Vector& v);
218
+ Vector operator-(const Vector& v) const;
219
+ Vector& operator-=(const Vector& v);
220
+ Vector operator*(const Vector& v) const;
221
+ Vector& operator*=(const Vector& v);
222
+ Vector operator/(const Vector& v) const;
223
+ Vector& operator/=(const Vector& v);
224
+
225
+ Vector operator*(float const f) const;
226
+ Vector& operator*=(float const f);
227
+ Vector operator/(float const f) const;
228
+ Vector& operator/=(float const f);
229
+
230
+ Vector operator-() const;
231
+
232
+ Vector Orthogonal2D() const;
233
+ Vector Cross2(const Vector& other) const;
234
+ Vector Cross3(const Vector& other) const;
235
+ Vector Dot2(const Vector& other) const;
236
+ Vector Dot3(const Vector& other) const;
237
+ Vector Dot4(const Vector& other) const;
238
+ float GetDot2(const Vector& other) const;
239
+ float GetDot3(const Vector& other) const;
240
+ float GetDot4(const Vector& other) const;
241
+
242
+ Vector ScalarProjection(const Vector& other) const;
243
+ float GetScalarProjection(const Vector& other) const;
244
+ Vector VectorProjection(const Vector& other) const;
245
+
246
+ //
247
+ // Transformations
248
+ //
249
+
250
+ Vector& Invert();
251
+ Vector GetInverse() const;
252
+ Vector GetReciprocal() const;
253
+
254
+ Vector& InvertEst();
255
+ Vector GetInverseEst() const;
256
+
257
+ Vector& Negate();
258
+ Vector GetNegated() const;
259
+
260
+ Vector& Abs();
261
+ Vector GetAbs() const;
262
+
263
+ Vector& Sqrt();
264
+ Vector GetSqrt();
265
+
266
+ Vector& ReciprocalSqrt();
267
+ Vector GetReciprocalSqrt();
268
+
269
+ Vector& EstimatedReciprocalSqrt();
270
+ Vector GetEstimatedReciprocalSqrt();
271
+
272
+ Vector& Normalize2();
273
+ Vector& Normalize3();
274
+ Vector& Normalize4();
275
+
276
+ Vector GetNormalized2() const;
277
+ Vector GetNormalized3() const;
278
+ Vector GetNormalized4() const;
279
+
280
+ Vector& Floor();
281
+ Vector GetFloor() const;
282
+ Vector& Ceil();
283
+ Vector GetCeil() const;
284
+ Vector& Round();
285
+ Vector GetRound() const;
286
+
287
+ Vector GetSign() const;
288
+
289
+ //
290
+ // Permutations
291
+ //
292
+
293
+ Vector GetSplatX() const;
294
+ Vector GetSplatY() const;
295
+ Vector GetSplatZ() const;
296
+ Vector GetSplatW() const;
297
+
298
+ // Get a shuffled version of this vector, each argument represents the element index in the original vector
299
+ template<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
300
+ Vector Swizzle() const;
301
+
302
+ // Get a shuffled version of this vector, each argument represents the element index in the original vector
303
+ Vector Swizzle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const;
304
+
305
+ // Get a shuffled version of this vector, each argument represents the element index in the original vector
306
+ Vector Shuffle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const;
307
+
308
+ // Get a shuffled version of this vector, each argument represents the element index in the original vector
309
+ template<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
310
+ Vector Shuffle() const;
311
+
312
+ //
313
+ // Queries
314
+ //
315
+
316
+ Vector Length2() const;
317
+ Vector Length3() const;
318
+ Vector Length4() const;
319
+
320
+ float GetLength2() const;
321
+ float GetLength3() const;
322
+ float GetLength4() const;
323
+
324
+ Vector InverseLength2() const;
325
+ Vector InverseLength3() const;
326
+ Vector InverseLength4() const;
327
+
328
+ float GetInverseLength2() const;
329
+ float GetInverseLength3() const;
330
+ float GetInverseLength4() const;
331
+
332
+ Vector LengthSquared2() const;
333
+ Vector LengthSquared3() const;
334
+ Vector LengthSquared4() const;
335
+
336
+ float GetLengthSquared2() const;
337
+ float GetLengthSquared3() const;
338
+ float GetLengthSquared4() const;
339
+
340
+ Vector Distance2(const Vector& to) const;
341
+ Vector Distance3(const Vector& to) const;
342
+ Vector Distance4(const Vector& to) const;
343
+
344
+ float GetDistance2(const Vector& to) const;
345
+ float GetDistance3(const Vector& to) const;
346
+ float GetDistance4(const Vector& to) const;
347
+
348
+ Vector DistanceSquared2(const Vector& to) const;
349
+ Vector DistanceSquared3(const Vector& to) const;
350
+ Vector DistanceSquared4(const Vector& to) const;
351
+
352
+ float GetDistanceSquared2(const Vector& to) const;
353
+ float GetDistanceSquared3(const Vector& to) const;
354
+ float GetDistanceSquared4(const Vector& to) const;
355
+
356
+ bool IsNormalized2() const;
357
+ bool IsNormalized3() const;
358
+ bool IsNormalized4() const;
359
+
360
+ // Is this vector within the range [-bounds, bounds]
361
+ Vector InBounds(const Vector& bounds) const;
362
+
363
+ bool IsInBounds2(const Vector& bounds) const;
364
+ bool IsInBounds3(const Vector& bounds) const;
365
+ bool IsInBounds4(const Vector& bounds) const;
366
+
367
+ Vector Equal(const Vector& v) const;
368
+
369
+ bool IsEqual2(const Vector& v) const;
370
+ bool IsEqual3(const Vector& v) const;
371
+ bool IsEqual4(const Vector& v) const;
372
+
373
+ Vector NearEqual(const Vector& v, const Vector& epsilon) const;
374
+
375
+ bool IsNearEqual2(const Vector& v, float epsilon) const;
376
+ bool IsNearEqual3(const Vector& v, float epsilon) const;
377
+ bool IsNearEqual4(const Vector& v, float epsilon) const;
378
+
379
+ bool IsNearEqual2(const Vector& v, const Vector& epsilon = Vector::Epsilon) const;
380
+ bool IsNearEqual3(const Vector& v, const Vector& epsilon = Vector::Epsilon) const;
381
+ bool IsNearEqual4(const Vector& v, const Vector& epsilon = Vector::Epsilon) const;
382
+
383
+ Vector GreaterThan(const Vector& v) const;
384
+ bool IsAnyGreaterThan(const Vector& v) const;
385
+
386
+ bool IsGreaterThan2(const Vector& v) const;
387
+ bool IsGreaterThan3(const Vector& v) const;
388
+ bool IsGreaterThan4(const Vector& v) const;
389
+
390
+ Vector GreaterThanEqual(const Vector& v) const;
391
+ bool IsAnyGreaterThanEqual(const Vector& v) const;
392
+
393
+ bool IsGreaterThanEqual2(const Vector& v) const;
394
+ bool IsGreaterThanEqual3(const Vector& v) const;
395
+ bool IsGreaterThanEqual4(const Vector& v) const;
396
+
397
+ Vector LessThan(const Vector& v) const;
398
+ bool IsAnyLessThan(const Vector& v) const;
399
+
400
+ bool IsLessThan2(const Vector& v) const;
401
+ bool IsLessThan3(const Vector& v) const;
402
+ bool IsLessThan4(const Vector& v) const;
403
+
404
+ Vector LessThanEqual(const Vector& v) const;
405
+ bool IsAnyLessThanEqual(const Vector& v) const;
406
+
407
+ bool IsLessThanEqual2(const Vector& v) const;
408
+ bool IsLessThanEqual3(const Vector& v) const;
409
+ bool IsLessThanEqual4(const Vector& v) const;
410
+
411
+ Vector EqualsZero() const;
412
+ bool IsAnyEqualToZero2() const;
413
+ bool IsAnyEqualToZero3() const;
414
+ bool IsAnyEqualToZero4() const;
415
+
416
+ bool IsZero2() const;
417
+ bool IsZero3() const;
418
+ bool IsZero4() const;
419
+
420
+ Vector NearEqualsZero(float epsilon = Math::Epsilon) const;
421
+
422
+ bool IsNearZero2(float epsilon = Math::Epsilon) const;
423
+ bool IsNearZero3(float epsilon = Math::Epsilon) const;
424
+ bool IsNearZero4(float epsilon = Math::Epsilon) const;
425
+
426
+ Vector EqualsInfinity() const;
427
+
428
+ bool IsInfinite2() const;
429
+ bool IsInfinite3() const;
430
+ bool IsInfinite4() const;
431
+
432
+ Vector EqualsNaN() const;
433
+
434
+ bool IsNaN2() const;
435
+ bool IsNaN3() const;
436
+ bool IsNaN4() const;
437
+
438
+ bool IsParallelTo(const Vector& v) const;
439
+
440
+ void ToDirectionAndLength2(Vector& direction, float& length) const;
441
+ void ToDirectionAndLength3(Vector& direction, float& length) const;
442
+
443
+ bool operator==(const Vector& rhs) const;
444
+ bool operator!=(const Vector& rhs) const;
445
+
446
+ public:
447
+
448
+ __m128 m_data;
449
+ };
450
+
451
+ static_assert(sizeof(Vector) == 16, "Vector size must be 16 bytes!");
452
+ }
453
+
454
+ #include "Vector.inl"
MotionCorrection/src/cpp/Math/Vector.inl ADDED
@@ -0,0 +1,2224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ #include <cstring>
9
+
10
+ #include "Vector.h"
11
+
12
+ namespace Math
13
+ {
14
+ FORCE_INLINE Vector Vector::Cross2(const Vector& v0, const Vector& v1)
15
+ {
16
+ return v0.Cross2(v1);
17
+ }
18
+
19
+ FORCE_INLINE Vector Vector::Cross3(const Vector& v0, const Vector& v1)
20
+ {
21
+ return v0.Cross3(v1);
22
+ }
23
+
24
+ FORCE_INLINE Vector Vector::Dot2(const Vector& v0, const Vector& v1)
25
+ {
26
+ return v0.Dot2(v1);
27
+ }
28
+
29
+ FORCE_INLINE Vector Vector::Dot3(const Vector& v0, const Vector& v1)
30
+ {
31
+ return v0.Dot3(v1);
32
+ }
33
+
34
+ FORCE_INLINE Vector Vector::Dot4(const Vector& v0, const Vector& v1)
35
+ {
36
+ return v0.Dot4(v1);
37
+ }
38
+
39
+ FORCE_INLINE Vector Vector::Average2(const Vector& v0, const Vector& v1)
40
+ {
41
+ auto avg4 = Average4(v0, v1);
42
+ return Vector::Select(avg4, Vector::Zero, Vector(0, 0, 1, 1));
43
+ }
44
+
45
+ FORCE_INLINE Vector Vector::Average3(const Vector& v0, const Vector& v1)
46
+ {
47
+ auto avg4 = Average4(v0, v1);
48
+ return Vector::Select(avg4, Vector::Zero, Vector(0, 0, 0, 1));
49
+ }
50
+
51
+ FORCE_INLINE Vector Vector::Average4(const Vector& v0, const Vector& v1)
52
+ {
53
+ return (v0 + v1) * Vector::Half;
54
+ }
55
+
56
+ FORCE_INLINE Vector Vector::Min(const Vector& v0, const Vector& v1)
57
+ {
58
+ Vector result;
59
+ result = _mm_min_ps(v0, v1);
60
+ return result;
61
+ }
62
+
63
+ FORCE_INLINE Vector Vector::Max(const Vector& v0, const Vector& v1)
64
+ {
65
+ Vector result;
66
+ result = _mm_max_ps(v0, v1);
67
+ return result;
68
+ }
69
+
70
+ FORCE_INLINE float Vector::Min(const Vector& v)
71
+ {
72
+ __m128 shufReg, sumsReg;
73
+ shufReg = _mm_movehdup_ps(v);
74
+ sumsReg = _mm_min_ps(v, shufReg);
75
+ shufReg = _mm_movehl_ps(shufReg, sumsReg);
76
+ sumsReg = _mm_min_ss(sumsReg, shufReg);
77
+ return _mm_cvtss_f32(sumsReg);
78
+ }
79
+
80
+ FORCE_INLINE float Vector::Max(const Vector& v)
81
+ {
82
+ __m128 shufReg, sumsReg;
83
+ shufReg = _mm_movehdup_ps(v);
84
+ sumsReg = _mm_max_ps(v, shufReg);
85
+ shufReg = _mm_movehl_ps(shufReg, sumsReg);
86
+ sumsReg = _mm_max_ss(sumsReg, shufReg);
87
+ return _mm_cvtss_f32(sumsReg);
88
+ }
89
+
90
+ FORCE_INLINE Vector Vector::Clamp(const Vector& v, const Vector& min, const Vector& max)
91
+ {
92
+ Vector result;
93
+ result = _mm_max_ps(min, v);
94
+ result = _mm_min_ps(result, max);
95
+ return result;
96
+ }
97
+
98
+ FORCE_INLINE Vector Vector::Xor(const Vector& v0, const Vector& v1)
99
+ {
100
+ __m128i V = _mm_xor_si128(_mm_castps_si128(v0), _mm_castps_si128(v1));
101
+
102
+ Vector result;
103
+ result = _mm_castsi128_ps(V);
104
+ return result;
105
+ }
106
+
107
+ FORCE_INLINE Vector Vector::MultiplyAdd(const Vector& v, const Vector& multiplier, const Vector& addend)
108
+ {
109
+ // result = addend + ( vec * multiplier )
110
+ Vector result;
111
+ result = _mm_mul_ps(v, multiplier);
112
+ result = _mm_add_ps(result, addend);
113
+ return result;
114
+ }
115
+
116
+ FORCE_INLINE Vector Vector::MultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& subtrahend)
117
+ {
118
+ // result = ( vec * multiplier ) - subtrahend
119
+ auto r = _mm_mul_ps(vec, multiplier);
120
+ return _mm_sub_ps(r, subtrahend);
121
+ }
122
+
123
+ FORCE_INLINE Vector Vector::NegativeMultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& minuend)
124
+ {
125
+ // result = minuend - ( vec * multiplier )
126
+ auto r = _mm_mul_ps(vec, multiplier);
127
+ return _mm_sub_ps(minuend, r);
128
+ }
129
+
130
+ FORCE_INLINE Vector Vector::LinearCombination(const Vector& v0, const Vector& v1, float scale0, float scale1)
131
+ {
132
+ return (v0 * scale0) + (v1 * scale1);
133
+ }
134
+
135
+ FORCE_INLINE Vector Vector::Lerp(const Vector& from, const Vector& to, float t)
136
+ {
137
+ ASSERT(t >= 0.0f && t <= 1.0f);
138
+
139
+ Vector L = _mm_sub_ps(to, from);
140
+ Vector S = _mm_set_ps1(t);
141
+
142
+ Vector result;
143
+ result = _mm_mul_ps(L, S);
144
+ result = _mm_add_ps(result, from);
145
+ return result;
146
+ }
147
+
148
+ FORCE_INLINE Vector Vector::NLerp(const Vector& from, const Vector& to, float t)
149
+ {
150
+ ASSERT(t >= 0.0f && t <= 1.0f);
151
+
152
+ // Calculate the final length
153
+ auto const fromLength = from.Length3();
154
+ auto const toLength = to.Length3();
155
+ auto const finalLength = Vector::Lerp(fromLength, toLength, t);
156
+
157
+ // Normalize vectors
158
+ Vector const normalizedFrom = from / fromLength;
159
+ Vector const normalizedTo = to / toLength;
160
+
161
+ // LERP
162
+ auto const finalDirection = Lerp(normalizedFrom, normalizedTo, t);
163
+ auto result = finalDirection.GetNormalized3() * finalLength;
164
+ return result;
165
+ }
166
+
167
+ FORCE_INLINE Vector Vector::Select(const Vector& v0, const Vector& v1, const Vector& control)
168
+ {
169
+ auto const ctrl = _mm_cmpneq_ps(control, Vector::Zero);
170
+
171
+ Vector result;
172
+ auto vTemp1 = _mm_andnot_ps(ctrl, v0);
173
+ auto vTemp2 = _mm_and_ps(v1, ctrl);
174
+ result = _mm_or_ps(vTemp1, vTemp2);
175
+ return result;
176
+ }
177
+
178
+ template<uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW>
179
+ FORCE_INLINE Vector Vector::Permute(const Vector& v0, const Vector& v1)
180
+ {
181
+ static_assert(PermuteX <= 7, "Element index parameter out of range");
182
+ static_assert(PermuteY <= 7, "Element index parameter out of range");
183
+ static_assert(PermuteZ <= 7, "Element index parameter out of range");
184
+ static_assert(PermuteW <= 7, "Element index parameter out of range");
185
+
186
+ uint32_t const shuffle = _MM_SHUFFLE(PermuteW & 3, PermuteZ & 3, PermuteY & 3, PermuteX & 3);
187
+ bool const whichX = PermuteX > 3;
188
+ bool const whichY = PermuteY > 3;
189
+ bool const whichZ = PermuteZ > 3;
190
+ bool const whichW = PermuteW > 3;
191
+
192
+ static SIMD::UIntMask const selectMask = { whichX ? 0xFFFFFFFF : 0, whichY ? 0xFFFFFFFF : 0, whichZ ? 0xFFFFFFFF : 0, whichW ? 0xFFFFFFFF : 0 };
193
+ __m128 shuffled1 = _mm_shuffle_ps(v0, v0, shuffle);
194
+ __m128 shuffled2 = _mm_shuffle_ps(v1, v1, shuffle);
195
+ __m128 masked1 = _mm_andnot_ps(selectMask, shuffled1);
196
+ __m128 masked2 = _mm_and_ps(selectMask, shuffled2);
197
+ return _mm_or_ps(masked1, masked2);
198
+ }
199
+
200
+ FORCE_INLINE Vector Vector::Sin(const Vector& vec)
201
+ {
202
+ // Force the value within the bounds of pi
203
+ auto m_x = Vector::AngleMod2Pi(vec);
204
+
205
+ // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x).
206
+ __m128 sign = _mm_and_ps(m_x, SIMD::g_signMask);
207
+ __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0
208
+ __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x|
209
+ __m128 rflx = _mm_sub_ps(c, m_x);
210
+ __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo);
211
+ __m128 select0 = _mm_and_ps(comp, m_x);
212
+ __m128 select1 = _mm_andnot_ps(comp, rflx);
213
+ m_x = _mm_or_ps(select0, select1);
214
+
215
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
216
+
217
+ // Compute polynomial approximation
218
+ const auto SC1 = SIMD::g_sinCoefficients1;
219
+ auto vConstants = _mm_shuffle_ps(SC1, SC1, _MM_SHUFFLE(0, 0, 0, 0));
220
+ __m128 Result = _mm_mul_ps(vConstants, x2);
221
+
222
+ const auto SC0 = SIMD::g_sinCoefficients0;
223
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(3, 3, 3, 3));
224
+ Result = _mm_add_ps(Result, vConstants);
225
+ Result = _mm_mul_ps(Result, x2);
226
+
227
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(2, 2, 2, 2));
228
+ Result = _mm_add_ps(Result, vConstants);
229
+ Result = _mm_mul_ps(Result, x2);
230
+
231
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(1, 1, 1, 1));
232
+ Result = _mm_add_ps(Result, vConstants);
233
+ Result = _mm_mul_ps(Result, x2);
234
+
235
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(0, 0, 0, 0));
236
+ Result = _mm_add_ps(Result, vConstants);
237
+ Result = _mm_mul_ps(Result, x2);
238
+ Result = _mm_add_ps(Result, Vector::One);
239
+ Result = _mm_mul_ps(Result, m_x);
240
+ return Result;
241
+ }
242
+
243
+ FORCE_INLINE Vector Vector::Cos(const Vector& vec)
244
+ {
245
+ // Map V to m_x in [-pi,pi].
246
+ auto m_x = Vector::AngleMod2Pi(vec);
247
+
248
+ // Map in [-pi/2,pi/2] with cos(m_y) = sign*cos(m_x).
249
+ auto sign = _mm_and_ps(m_x, SIMD::g_signMask);
250
+ __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0
251
+ __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x|
252
+ __m128 rflx = _mm_sub_ps(c, m_x);
253
+ __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo);
254
+ __m128 select0 = _mm_and_ps(comp, m_x);
255
+ __m128 select1 = _mm_andnot_ps(comp, rflx);
256
+ m_x = _mm_or_ps(select0, select1);
257
+ select0 = _mm_and_ps(comp, Vector::One);
258
+ select1 = _mm_andnot_ps(comp, Vector::NegativeOne);
259
+ sign = _mm_or_ps(select0, select1);
260
+
261
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
262
+
263
+ // Compute polynomial approximation
264
+ const auto CC1 = SIMD::g_cosCoefficients1;
265
+ auto vConstants = _mm_shuffle_ps(CC1, CC1, _MM_SHUFFLE(0, 0, 0, 0));
266
+ __m128 Result = _mm_mul_ps(vConstants, x2);
267
+
268
+ const auto CC0 = SIMD::g_cosCoefficients0;
269
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(3, 3, 3, 3));
270
+ Result = _mm_add_ps(Result, vConstants);
271
+ Result = _mm_mul_ps(Result, x2);
272
+
273
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(2, 2, 2, 2));
274
+ Result = _mm_add_ps(Result, vConstants);
275
+ Result = _mm_mul_ps(Result, x2);
276
+
277
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(1, 1, 1, 1));
278
+ Result = _mm_add_ps(Result, vConstants);
279
+ Result = _mm_mul_ps(Result, x2);
280
+
281
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(0, 0, 0, 0));
282
+ Result = _mm_add_ps(Result, vConstants);
283
+ Result = _mm_mul_ps(Result, x2);
284
+ Result = _mm_add_ps(Result, Vector::One);
285
+ Result = _mm_mul_ps(Result, sign);
286
+ return Result;
287
+ }
288
+
289
+ FORCE_INLINE Vector Vector::Tan(const Vector& vec)
290
+ {
291
+ static const Vector tanCoefficients0 = { 1.0f, -4.667168334e-1f, 2.566383229e-2f, -3.118153191e-4f };
292
+ static const Vector tanCoefficients1 = { 4.981943399e-7f, -1.333835001e-1f, 3.424887824e-3f, -1.786170734e-5f };
293
+ static const Vector tanConstants = { 1.570796371f, 6.077100628e-11f, 0.000244140625f, 0.63661977228f /*2 / Pi*/ };
294
+ static const SIMD::UIntMask mask = { 0x1, 0x1, 0x1, 0x1 };
295
+
296
+ Vector TwoDivPi = tanConstants.GetSplatW();
297
+ Vector C0 = tanConstants.GetSplatX();
298
+ Vector C1 = tanConstants.GetSplatY();
299
+ Vector vEpsilon = tanConstants.GetSplatZ();
300
+
301
+ Vector VA = (vec * TwoDivPi).Round();
302
+ Vector VC = Vector::NegativeMultiplySubtract(VA, C0, vec);
303
+ Vector VB = VA.GetAbs();
304
+ VC = Vector::NegativeMultiplySubtract(VA, C1, VC);
305
+ reinterpret_cast<__m128i*>(&VB)[0] = _mm_cvttps_epi32(VB);
306
+
307
+ Vector VC2 = VC * VC;
308
+ Vector T7 = tanCoefficients1.GetSplatW();
309
+ Vector T6 = tanCoefficients1.GetSplatZ();
310
+ Vector T4 = tanCoefficients1.GetSplatX();
311
+ Vector T3 = tanCoefficients0.GetSplatW();
312
+ Vector T5 = tanCoefficients1.GetSplatY();
313
+ Vector T2 = tanCoefficients0.GetSplatZ();
314
+ Vector T1 = tanCoefficients0.GetSplatY();
315
+ Vector T0 = tanCoefficients0.GetSplatX();
316
+
317
+ Vector VBIsEven = _mm_and_ps(VB, mask);
318
+ VBIsEven = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(VBIsEven), _mm_castps_si128(Vector::Zero)));
319
+
320
+ Vector N = Vector::MultiplyAdd(VC2, T7, T6);
321
+ Vector D = Vector::MultiplyAdd(VC2, T4, T3);
322
+ N = Vector::MultiplyAdd(VC2, N, T5);
323
+ D = Vector::MultiplyAdd(VC2, D, T2);
324
+ N = VC2 * N;
325
+ D = Vector::MultiplyAdd(VC2, D, T1);
326
+ N = Vector::MultiplyAdd(VC, N, VC);
327
+ Vector VCNearZero = VC.InBounds(vEpsilon);
328
+ D = Vector::MultiplyAdd(VC2, D, T0);
329
+
330
+ N = Vector::Select(N, VC, VCNearZero);
331
+ D = Vector::Select(D, Vector::One, VCNearZero);
332
+
333
+ Vector R0 = N.GetNegated();
334
+ Vector R1 = N / D;
335
+ R0 = D / R0;
336
+
337
+ Vector VIsZero = vec.EqualsZero();
338
+ Vector Result = Vector::Select(R0, R1, VBIsEven);
339
+ Result = Vector::Select(Result, Zero, VIsZero);
340
+
341
+ return Result;
342
+ }
343
+
344
+ FORCE_INLINE Vector Vector::ASin(const Vector& vec)
345
+ {
346
+ __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero);
347
+ __m128 mvalue = _mm_sub_ps(Vector::Zero, vec);
348
+ __m128 m_x = _mm_max_ps(vec, mvalue); // |vec|
349
+
350
+ // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number.
351
+ __m128 oneMValue = _mm_sub_ps(Vector::One, m_x);
352
+ __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue);
353
+ __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|)
354
+
355
+ // Compute polynomial approximation
356
+ const auto AC1 = SIMD::g_arcCoefficients1;
357
+ auto vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(3, 3, 3, 3));
358
+ __m128 t0 = _mm_mul_ps(vConstants, m_x);
359
+
360
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(2, 2, 2, 2));
361
+ t0 = _mm_add_ps(t0, vConstants);
362
+ t0 = _mm_mul_ps(t0, m_x);
363
+
364
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(1, 1, 1, 1));
365
+ t0 = _mm_add_ps(t0, vConstants);
366
+ t0 = _mm_mul_ps(t0, m_x);
367
+
368
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(0, 0, 0, 0));
369
+ t0 = _mm_add_ps(t0, vConstants);
370
+ t0 = _mm_mul_ps(t0, m_x);
371
+
372
+ const auto AC0 = SIMD::g_arcCoefficients0;
373
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(3, 3, 3, 3));
374
+ t0 = _mm_add_ps(t0, vConstants);
375
+ t0 = _mm_mul_ps(t0, m_x);
376
+
377
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(2, 2, 2, 2));
378
+ t0 = _mm_add_ps(t0, vConstants);
379
+ t0 = _mm_mul_ps(t0, m_x);
380
+
381
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(1, 1, 1, 1));
382
+ t0 = _mm_add_ps(t0, vConstants);
383
+ t0 = _mm_mul_ps(t0, m_x);
384
+
385
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(0, 0, 0, 0));
386
+ t0 = _mm_add_ps(t0, vConstants);
387
+ t0 = _mm_mul_ps(t0, root);
388
+
389
+ __m128 t1 = _mm_sub_ps(Vector::Pi, t0);
390
+ t0 = _mm_and_ps(nonnegative, t0);
391
+ t1 = _mm_andnot_ps(nonnegative, t1);
392
+ t0 = _mm_or_ps(t0, t1);
393
+ t0 = _mm_sub_ps(Vector::PiDivTwo, t0);
394
+ return t0;
395
+ }
396
+
397
+ FORCE_INLINE Vector Vector::ACos(const Vector& vec)
398
+ {
399
+ __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero);
400
+ __m128 mvalue = _mm_sub_ps(Vector::Zero, vec);
401
+ __m128 m_x = _mm_max_ps(vec, mvalue); // |vec|
402
+
403
+ // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number.
404
+ __m128 oneMValue = _mm_sub_ps(Vector::One, m_x);
405
+ __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue);
406
+ __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|)
407
+
408
+ // Compute polynomial approximation
409
+ const auto AC1 = SIMD::g_arcCoefficients1;
410
+ auto vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(3, 3, 3, 3));
411
+ __m128 t0 = _mm_mul_ps(vConstants, m_x);
412
+
413
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(2, 2, 2, 2));
414
+ t0 = _mm_add_ps(t0, vConstants);
415
+ t0 = _mm_mul_ps(t0, m_x);
416
+
417
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(1, 1, 1, 1));
418
+ t0 = _mm_add_ps(t0, vConstants);
419
+ t0 = _mm_mul_ps(t0, m_x);
420
+
421
+ vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(0, 0, 0, 0));
422
+ t0 = _mm_add_ps(t0, vConstants);
423
+ t0 = _mm_mul_ps(t0, m_x);
424
+
425
+ const auto AC0 = SIMD::g_arcCoefficients0;
426
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(3, 3, 3, 3));
427
+ t0 = _mm_add_ps(t0, vConstants);
428
+ t0 = _mm_mul_ps(t0, m_x);
429
+
430
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(2, 2, 2, 2));
431
+ t0 = _mm_add_ps(t0, vConstants);
432
+ t0 = _mm_mul_ps(t0, m_x);
433
+
434
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(1, 1, 1, 1));
435
+ t0 = _mm_add_ps(t0, vConstants);
436
+ t0 = _mm_mul_ps(t0, m_x);
437
+
438
+ vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(0, 0, 0, 0));
439
+ t0 = _mm_add_ps(t0, vConstants);
440
+ t0 = _mm_mul_ps(t0, root);
441
+
442
+ __m128 t1 = _mm_sub_ps(Vector::Pi, t0);
443
+ t0 = _mm_and_ps(nonnegative, t0);
444
+ t1 = _mm_andnot_ps(nonnegative, t1);
445
+ t0 = _mm_or_ps(t0, t1);
446
+ return t0;
447
+ }
448
+
449
+ FORCE_INLINE Vector Vector::ATan(const Vector& vec)
450
+ {
451
+ __m128 absV = vec.GetAbs();
452
+ __m128 invV = _mm_div_ps(Vector::One, vec);
453
+ __m128 comp = _mm_cmpgt_ps(vec, Vector::One);
454
+ __m128 select0 = _mm_and_ps(comp, Vector::One);
455
+ __m128 select1 = _mm_andnot_ps(comp, Vector::NegativeOne);
456
+ __m128 sign = _mm_or_ps(select0, select1);
457
+ comp = _mm_cmple_ps(absV, Vector::One);
458
+ select0 = _mm_and_ps(comp, Vector::Zero);
459
+ select1 = _mm_andnot_ps(comp, sign);
460
+ sign = _mm_or_ps(select0, select1);
461
+ select0 = _mm_and_ps(comp, vec);
462
+ select1 = _mm_andnot_ps(comp, invV);
463
+ __m128 m_x = _mm_or_ps(select0, select1);
464
+
465
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
466
+
467
+ // Compute polynomial approximation
468
+ Vector const TC1 = SIMD::g_aTanCoefficients1;
469
+ Vector vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(3, 3, 3, 3));
470
+ __m128 Result = _mm_mul_ps(vConstants, x2);
471
+
472
+ vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(2, 2, 2, 2));
473
+ Result = _mm_add_ps(Result, vConstants);
474
+ Result = _mm_mul_ps(Result, x2);
475
+
476
+ vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(1, 1, 1, 1));
477
+ Result = _mm_add_ps(Result, vConstants);
478
+ Result = _mm_mul_ps(Result, x2);
479
+
480
+ vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(0, 0, 0, 0));
481
+ Result = _mm_add_ps(Result, vConstants);
482
+ Result = _mm_mul_ps(Result, x2);
483
+
484
+ Vector const TC0 = SIMD::g_aTanCoefficients0;
485
+ vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(3, 3, 3, 3));
486
+ Result = _mm_add_ps(Result, vConstants);
487
+ Result = _mm_mul_ps(Result, x2);
488
+
489
+ vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(2, 2, 2, 2));
490
+ Result = _mm_add_ps(Result, vConstants);
491
+ Result = _mm_mul_ps(Result, x2);
492
+
493
+ vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(1, 1, 1, 1));
494
+ Result = _mm_add_ps(Result, vConstants);
495
+ Result = _mm_mul_ps(Result, x2);
496
+
497
+ vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(0, 0, 0, 0));
498
+ Result = _mm_add_ps(Result, vConstants);
499
+ Result = _mm_mul_ps(Result, x2);
500
+ Result = _mm_add_ps(Result, Vector::One);
501
+ Result = _mm_mul_ps(Result, m_x);
502
+ __m128 result1 = _mm_mul_ps(sign, Vector::PiDivTwo);
503
+ result1 = _mm_sub_ps(result1, Result);
504
+
505
+ comp = _mm_cmpeq_ps(sign, Vector::Zero);
506
+ select0 = _mm_and_ps(comp, Result);
507
+ select1 = _mm_andnot_ps(comp, result1);
508
+ Result = _mm_or_ps(select0, select1);
509
+ return Result;
510
+ }
511
+
512
+ FORCE_INLINE Vector Vector::ATan2(const Vector& Y, const Vector& X)
513
+ {
514
+ Vector ATanResultValid = Vector(SIMD::g_trueMask);
515
+
516
+ Vector vPi = Vector(SIMD::g_aTan2Constants).GetSplatX();
517
+ Vector vPiOverTwo = Vector(SIMD::g_aTan2Constants).GetSplatY();
518
+ Vector vPiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatZ();
519
+ Vector vThreePiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatW();
520
+
521
+ Vector YEqualsZero = Y.EqualsZero();
522
+ Vector XEqualsZero = X.EqualsZero();
523
+ Vector XIsPositive = _mm_and_ps(X, SIMD::g_signMask);
524
+ XIsPositive = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(XIsPositive), _mm_castps_si128(Vector::Zero)));
525
+ Vector YEqualsInfinity = Y.EqualsInfinity();
526
+ Vector XEqualsInfinity = X.EqualsInfinity();
527
+
528
+ Vector YSign = _mm_and_ps(Y, SIMD::g_signMask);
529
+ vPi = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPi), _mm_castps_si128(YSign)));
530
+ vPiOverTwo = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverTwo), _mm_castps_si128(YSign)));
531
+ vPiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverFour), _mm_castps_si128(YSign)));
532
+ vThreePiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vThreePiOverFour), _mm_castps_si128(YSign)));
533
+
534
+ Vector R1 = Vector::Select(vPi, YSign, XIsPositive);
535
+ Vector R2 = Vector::Select(ATanResultValid, vPiOverTwo, XEqualsZero);
536
+ Vector R3 = Vector::Select(R2, R1, YEqualsZero);
537
+ Vector R4 = Vector::Select(vThreePiOverFour, vPiOverFour, XIsPositive);
538
+ Vector R5 = Vector::Select(vPiOverTwo, R4, XEqualsInfinity);
539
+ Vector Result = Vector::Select(R3, R5, YEqualsInfinity);
540
+ ATanResultValid = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(Result), _mm_castps_si128(ATanResultValid)));
541
+
542
+ Vector V = Y / X;
543
+ Vector R0 = Vector::ATan(V);
544
+ R1 = Vector::Select(vPi, Vector(SIMD::g_signMask), XIsPositive);
545
+ R2 = R0 + R1;
546
+
547
+ return Vector::Select(Result, R2, ATanResultValid);
548
+ }
549
+
550
+ FORCE_INLINE Vector Vector::SinEst(const Vector& vec)
551
+ {
552
+ // Force the value within the bounds of pi
553
+ auto m_x = Vector::AngleMod2Pi(vec);
554
+
555
+ // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x).
556
+ __m128 sign = _mm_and_ps(m_x, SIMD::g_signMask);
557
+ __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0
558
+ __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x|
559
+ __m128 rflx = _mm_sub_ps(c, m_x);
560
+ __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo);
561
+ __m128 select0 = _mm_and_ps(comp, m_x);
562
+ __m128 select1 = _mm_andnot_ps(comp, rflx);
563
+ m_x = _mm_or_ps(select0, select1);
564
+
565
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
566
+
567
+ // Compute polynomial approximation
568
+ const auto SEC = SIMD::g_sinCoefficients1;
569
+ auto vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(3, 3, 3, 3));
570
+ __m128 Result = _mm_mul_ps(vConstants, x2);
571
+
572
+ vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(2, 2, 2, 2));
573
+ Result = _mm_add_ps(Result, vConstants);
574
+ Result = _mm_mul_ps(Result, x2);
575
+
576
+ vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(1, 1, 1, 1));
577
+ Result = _mm_add_ps(Result, vConstants);
578
+ Result = _mm_mul_ps(Result, x2);
579
+
580
+ Result = _mm_add_ps(Result, Vector::One);
581
+ Result = _mm_mul_ps(Result, m_x);
582
+ return Result;
583
+ }
584
+
585
+ FORCE_INLINE Vector Vector::CosEst(const Vector& vec)
586
+ {
587
+ // Map V to m_x in [-pi,pi].
588
+ auto m_x = Vector::AngleMod2Pi(vec);
589
+
590
+ // Map in [-pi/2,pi/2] with cos(m_y) = sign*cos(m_x).
591
+ auto sign = _mm_and_ps(m_x, SIMD::g_signMask);
592
+ __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0
593
+ __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x|
594
+ __m128 rflx = _mm_sub_ps(c, m_x);
595
+ __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo);
596
+ __m128 select0 = _mm_and_ps(comp, m_x);
597
+ __m128 select1 = _mm_andnot_ps(comp, rflx);
598
+ m_x = _mm_or_ps(select0, select1);
599
+ select0 = _mm_and_ps(comp, Vector::One);
600
+ select1 = _mm_andnot_ps(comp, Vector::NegativeOne);
601
+ sign = _mm_or_ps(select0, select1);
602
+
603
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
604
+
605
+ // Compute polynomial approximation
606
+ const auto CEC = SIMD::g_cosCoefficients1;
607
+ auto vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(3, 3, 3, 3));
608
+ __m128 Result = _mm_mul_ps(vConstants, x2);
609
+
610
+ vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(2, 2, 2, 2));
611
+ Result = _mm_add_ps(Result, vConstants);
612
+ Result = _mm_mul_ps(Result, x2);
613
+
614
+ vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(1, 1, 1, 1));
615
+ Result = _mm_add_ps(Result, vConstants);
616
+ Result = _mm_mul_ps(Result, x2);
617
+
618
+ Result = _mm_add_ps(Result, Vector::One);
619
+ Result = _mm_mul_ps(Result, sign);
620
+ return Result;
621
+ }
622
+
623
+ FORCE_INLINE Vector Vector::TanEst(const Vector& vec)
624
+ {
625
+ Vector W = Vector(SIMD::g_tanEstCoefficients).GetSplatW();
626
+ Vector V1 = (vec * W).Round();
627
+ V1 = Vector::NegativeMultiplySubtract(Vector::Pi, V1, vec);
628
+
629
+ Vector const T0 = Vector(SIMD::g_tanEstCoefficients).GetSplatX();
630
+ Vector const T1 = Vector(SIMD::g_tanEstCoefficients).GetSplatY();
631
+ Vector const T2 = Vector(SIMD::g_tanEstCoefficients).GetSplatZ();
632
+
633
+ auto V2T2 = Vector::NegativeMultiplySubtract(V1, V1, T2);
634
+ auto V2 = V1 * V1;
635
+ auto V1T0 = V1 * T0;
636
+ auto V1T1 = V1 * T1;
637
+
638
+ auto N = Vector::MultiplyAdd(V2, V1T1, V1T0);
639
+ auto D = V2T2.GetInverseEst();
640
+ return N * D;
641
+ }
642
+
643
+ FORCE_INLINE Vector Vector::ASinEst(const Vector& vec)
644
+ {
645
+ __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero);
646
+ __m128 mvalue = _mm_sub_ps(Vector::Zero, vec);
647
+ __m128 m_x = _mm_max_ps(vec, mvalue); // |vec|
648
+
649
+ // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number.
650
+ __m128 oneMValue = _mm_sub_ps(Vector::One, m_x);
651
+ __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue);
652
+ __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|)
653
+
654
+ // Compute polynomial approximation
655
+ const auto AEC = SIMD::g_arcEstCoefficients;
656
+ auto vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(3, 3, 3, 3));
657
+ __m128 t0 = _mm_mul_ps(vConstants, m_x);
658
+
659
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(2, 2, 2, 2));
660
+ t0 = _mm_add_ps(t0, vConstants);
661
+ t0 = _mm_mul_ps(t0, m_x);
662
+
663
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(1, 1, 1, 1));
664
+ t0 = _mm_add_ps(t0, vConstants);
665
+ t0 = _mm_mul_ps(t0, m_x);
666
+
667
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(0, 0, 0, 0));
668
+ t0 = _mm_add_ps(t0, vConstants);
669
+ t0 = _mm_mul_ps(t0, root);
670
+
671
+ __m128 t1 = _mm_sub_ps(Vector::Pi, t0);
672
+ t0 = _mm_and_ps(nonnegative, t0);
673
+ t1 = _mm_andnot_ps(nonnegative, t1);
674
+ t0 = _mm_or_ps(t0, t1);
675
+ t0 = _mm_sub_ps(Vector::PiDivTwo, t0);
676
+ return t0;
677
+ }
678
+
679
+ FORCE_INLINE Vector Vector::ACosEst(const Vector& vec)
680
+ {
681
+ __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero);
682
+ __m128 mvalue = _mm_sub_ps(Vector::Zero, vec);
683
+ __m128 m_x = _mm_max_ps(vec, mvalue); // |vec|
684
+
685
+ // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number.
686
+ __m128 oneMValue = _mm_sub_ps(Vector::One, m_x);
687
+ __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue);
688
+ __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|)
689
+
690
+ // Compute polynomial approximation
691
+ auto vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(3, 3, 3, 3));
692
+ __m128 t0 = _mm_mul_ps(vConstants, m_x);
693
+
694
+ vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(2, 2, 2, 2));
695
+ t0 = _mm_add_ps(t0, vConstants);
696
+ t0 = _mm_mul_ps(t0, m_x);
697
+
698
+ vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(1, 1, 1, 1));
699
+ t0 = _mm_add_ps(t0, vConstants);
700
+ t0 = _mm_mul_ps(t0, m_x);
701
+
702
+ vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(0, 0, 0, 0));
703
+ t0 = _mm_add_ps(t0, vConstants);
704
+ t0 = _mm_mul_ps(t0, root);
705
+
706
+ __m128 t1 = _mm_sub_ps(Vector::Pi, t0);
707
+ t0 = _mm_and_ps(nonnegative, t0);
708
+ t1 = _mm_andnot_ps(nonnegative, t1);
709
+ t0 = _mm_or_ps(t0, t1);
710
+ return t0;
711
+ }
712
+
713
+ FORCE_INLINE Vector Vector::ATanEst(const Vector& vec)
714
+ {
715
+ __m128 absV = vec.GetAbs();
716
+ __m128 invV = _mm_div_ps(Vector::One, vec);
717
+ __m128 comp = _mm_cmpgt_ps(vec, Vector::One);
718
+ __m128 select0 = _mm_and_ps(comp, Vector::One);
719
+ __m128 select1 = _mm_andnot_ps(comp, Vector::NegativeOne);
720
+ __m128 sign = _mm_or_ps(select0, select1);
721
+ comp = _mm_cmple_ps(absV, Vector::One);
722
+ select0 = _mm_and_ps(comp, Vector::Zero);
723
+ select1 = _mm_andnot_ps(comp, sign);
724
+ sign = _mm_or_ps(select0, select1);
725
+ select0 = _mm_and_ps(comp, vec);
726
+ select1 = _mm_andnot_ps(comp, invV);
727
+ __m128 m_x = _mm_or_ps(select0, select1);
728
+
729
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
730
+
731
+ // Compute polynomial approximation
732
+ Vector const AEC = SIMD::g_aTanEstCoefficients1;
733
+ Vector vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(3, 3, 3, 3));
734
+ __m128 Result = _mm_mul_ps(vConstants, x2);
735
+
736
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(2, 2, 2, 2));
737
+ Result = _mm_add_ps(Result, vConstants);
738
+ Result = _mm_mul_ps(Result, x2);
739
+
740
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(1, 1, 1, 1));
741
+ Result = _mm_add_ps(Result, vConstants);
742
+ Result = _mm_mul_ps(Result, x2);
743
+
744
+ vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(0, 0, 0, 0));
745
+ Result = _mm_add_ps(Result, vConstants);
746
+ Result = _mm_mul_ps(Result, x2);
747
+
748
+ // ATanEstCoefficients0 is already splatted
749
+ Result = _mm_add_ps(Result, SIMD::g_aTanEstCoefficients0);
750
+ Result = _mm_mul_ps(Result, m_x);
751
+ __m128 result1 = _mm_mul_ps(sign, Vector::PiDivTwo);
752
+ result1 = _mm_sub_ps(result1, Result);
753
+
754
+ comp = _mm_cmpeq_ps(sign, Vector::Zero);
755
+ select0 = _mm_and_ps(comp, Result);
756
+ select1 = _mm_andnot_ps(comp, result1);
757
+ Result = _mm_or_ps(select0, select1);
758
+ return Result;
759
+ }
760
+
761
+ FORCE_INLINE Vector Vector::ATan2Est(const Vector& X, const Vector& Y)
762
+ {
763
+ Vector ATanResultValid = Vector(SIMD::g_trueMask);
764
+
765
+ Vector vPi = Vector(SIMD::g_aTan2Constants).GetSplatX();
766
+ Vector vPiOverTwo = Vector(SIMD::g_aTan2Constants).GetSplatY();
767
+ Vector vPiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatZ();
768
+ Vector vThreePiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatW();
769
+
770
+ Vector YEqualsZero = Y.EqualsZero();
771
+ Vector XEqualsZero = X.EqualsZero();
772
+ Vector XIsPositive = _mm_and_ps(X, SIMD::g_signMask);
773
+ XIsPositive = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(XIsPositive), _mm_castps_si128(Vector::Zero)));
774
+ Vector YEqualsInfinity = Y.EqualsInfinity();
775
+ Vector XEqualsInfinity = X.EqualsInfinity();
776
+
777
+ Vector YSign = _mm_and_ps(Y, SIMD::g_signMask);
778
+ vPi = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPi), _mm_castps_si128(YSign)));
779
+ vPiOverTwo = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverTwo), _mm_castps_si128(YSign)));
780
+ vPiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverFour), _mm_castps_si128(YSign)));
781
+ vThreePiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vThreePiOverFour), _mm_castps_si128(YSign)));
782
+
783
+ Vector R1 = Vector::Select(vPi, YSign, XIsPositive);
784
+ Vector R2 = Vector::Select(ATanResultValid, vPiOverTwo, XEqualsZero);
785
+ Vector R3 = Vector::Select(R2, R1, YEqualsZero);
786
+ Vector R4 = Vector::Select(vThreePiOverFour, vPiOverFour, XIsPositive);
787
+ Vector R5 = Vector::Select(vPiOverTwo, R4, XEqualsInfinity);
788
+ Vector Result = Vector::Select(R3, R5, YEqualsInfinity);
789
+ ATanResultValid = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(Result), _mm_castps_si128(ATanResultValid)));
790
+
791
+ Vector Reciprocal = X.GetInverseEst();
792
+ Vector V = Y * Reciprocal;
793
+ Vector R0 = Vector::ATanEst(V);
794
+
795
+ R1 = Vector::Select(vPi, Vector(SIMD::g_signMask), XIsPositive);
796
+ R2 = R0 + R1;
797
+ Result = Vector::Select(Result, R2, ATanResultValid);
798
+
799
+ return Result;
800
+ }
801
+
802
+ FORCE_INLINE void Vector::SinCos(Vector& sin, Vector& cos, float angle)
803
+ {
804
+ return SinCos(sin, cos, Vector(angle));
805
+ }
806
+
807
+ FORCE_INLINE void Vector::SinCos(Vector& sin, Vector& cos, const Vector& angle)
808
+ {
809
+ // Force the value within the bounds of pi
810
+ auto m_x = Vector::AngleMod2Pi(angle);
811
+
812
+ // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x), cos(m_y) = sign*cos(m_x).
813
+ auto sign = _mm_and_ps(m_x, SIMD::g_signMask);
814
+ __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0
815
+ __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x|
816
+ __m128 rflx = _mm_sub_ps(c, m_x);
817
+ __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo);
818
+ __m128 select0 = _mm_and_ps(comp, m_x);
819
+ __m128 select1 = _mm_andnot_ps(comp, rflx);
820
+ m_x = _mm_or_ps(select0, select1);
821
+ select0 = _mm_and_ps(comp, Vector::One);
822
+ select1 = _mm_andnot_ps(comp, Vector::NegativeOne);
823
+ sign = _mm_or_ps(select0, select1);
824
+
825
+ __m128 x2 = _mm_mul_ps(m_x, m_x);
826
+
827
+ // Compute polynomial approximation of sine
828
+ const auto SC1 = SIMD::g_sinCoefficients1;
829
+ auto vConstants = _mm_shuffle_ps(SC1, SC1, _MM_SHUFFLE(0, 0, 0, 0));
830
+ __m128 Result = _mm_mul_ps(vConstants, x2);
831
+
832
+ const auto SC0 = SIMD::g_sinCoefficients0;
833
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(3, 3, 3, 3));
834
+ Result = _mm_add_ps(Result, vConstants);
835
+ Result = _mm_mul_ps(Result, x2);
836
+
837
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(2, 2, 2, 2));
838
+ Result = _mm_add_ps(Result, vConstants);
839
+ Result = _mm_mul_ps(Result, x2);
840
+
841
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(1, 1, 1, 1));
842
+ Result = _mm_add_ps(Result, vConstants);
843
+ Result = _mm_mul_ps(Result, x2);
844
+
845
+ vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(0, 0, 0, 0));
846
+ Result = _mm_add_ps(Result, vConstants);
847
+ Result = _mm_mul_ps(Result, x2);
848
+ Result = _mm_add_ps(Result, Vector::One);
849
+ Result = _mm_mul_ps(Result, m_x);
850
+ sin = Result;
851
+
852
+ // Compute polynomial approximation of cosine
853
+ const auto CC1 = SIMD::g_cosCoefficients1;
854
+ vConstants = _mm_shuffle_ps(CC1, CC1, _MM_SHUFFLE(0, 0, 0, 0));
855
+ Result = _mm_mul_ps(vConstants, x2);
856
+
857
+ const auto CC0 = SIMD::g_cosCoefficients0;
858
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(3, 3, 3, 3));
859
+ Result = _mm_add_ps(Result, vConstants);
860
+ Result = _mm_mul_ps(Result, x2);
861
+
862
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(2, 2, 2, 2));
863
+ Result = _mm_add_ps(Result, vConstants);
864
+ Result = _mm_mul_ps(Result, x2);
865
+
866
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(1, 1, 1, 1));
867
+ Result = _mm_add_ps(Result, vConstants);
868
+ Result = _mm_mul_ps(Result, x2);
869
+
870
+ vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(0, 0, 0, 0));
871
+ Result = _mm_add_ps(Result, vConstants);
872
+ Result = _mm_mul_ps(Result, x2);
873
+ Result = _mm_add_ps(Result, Vector::One);
874
+ Result = _mm_mul_ps(Result, sign);
875
+ cos = Result;
876
+ }
877
+
878
+ FORCE_INLINE Vector Vector::AngleMod2Pi(const Vector& angles)
879
+ {
880
+ // Modulo the range of the given angles such that -Pi <= Angles < Pi
881
+ Vector result = _mm_mul_ps(angles, Vector::OneDivTwoPi);
882
+ result.Round();
883
+ result = _mm_mul_ps(result, Vector::TwoPi);
884
+ result = _mm_sub_ps(angles, result);
885
+ return result;
886
+ }
887
+
888
+ FORCE_INLINE Vector::operator __m128& ()
889
+ {
890
+ return m_data;
891
+ }
892
+
893
+ FORCE_INLINE Vector::operator const __m128& () const
894
+ {
895
+ return m_data;
896
+ }
897
+
898
+ FORCE_INLINE Vector::Vector()
899
+ {
900
+ }
901
+
902
+ FORCE_INLINE Vector::Vector(Axis axis)
903
+ {
904
+ switch (axis)
905
+ {
906
+ case Axis::X: *this = Vector::UnitX; break;
907
+ case Axis::Y: *this = Vector::UnitY; break;
908
+ case Axis::Z: *this = Vector::UnitZ; break;
909
+ default: HALT(); break;
910
+ }
911
+ }
912
+
913
+ FORCE_INLINE Vector::Vector(ZeroInit_t)
914
+ {
915
+ memset(this, 0, sizeof(Vector));
916
+ }
917
+
918
+ FORCE_INLINE Vector::Vector(float v)
919
+ {
920
+ m_data = _mm_set1_ps(v);
921
+ }
922
+
923
+ FORCE_INLINE Vector::Vector(__m128 v)
924
+ : m_data(v)
925
+ {
926
+ }
927
+
928
+ FORCE_INLINE Vector::Vector(float ix, float iy, float iz, float iw)
929
+ {
930
+ m_data = _mm_set_ps(iw, iz, iy, ix);
931
+ }
932
+
933
+ FORCE_INLINE Vector::Vector(const Float2& v, float iz, float iw)
934
+ {
935
+ m_data = _mm_set_ps(iw, iz, v.m_y, v.m_x);
936
+ }
937
+
938
+ FORCE_INLINE Vector::Vector(const Float3& v, float iw)
939
+ {
940
+ m_data = _mm_set_ps(iw, v.m_z, v.m_y, v.m_x);
941
+ }
942
+
943
+ FORCE_INLINE Vector::Vector(const Float4& v)
944
+ {
945
+ m_data = _mm_loadu_ps(&v.m_x);
946
+ }
947
+
948
+ FORCE_INLINE Vector::Vector(const float* pValues)
949
+ {
950
+ m_data = _mm_loadu_ps(pValues);
951
+ }
952
+
953
+ FORCE_INLINE bool Vector::IsValid() const
954
+ {
955
+ return !IsNaN4() && !IsInfinite4();
956
+ }
957
+
958
+ FORCE_INLINE void Vector::Store(float* pValues) const
959
+ {
960
+ _mm_storeu_ps(pValues, m_data);
961
+ }
962
+
963
+ FORCE_INLINE void Vector::StoreFloat(float& value) const
964
+ {
965
+ _mm_store_ss(&value, m_data);
966
+ }
967
+
968
+ FORCE_INLINE void Vector::StoreFloat2(Float2& value) const
969
+ {
970
+ auto yVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1));
971
+ _mm_store_ss(&value.m_x, m_data);
972
+ _mm_store_ss(&value.m_y, yVec);
973
+ }
974
+
975
+ FORCE_INLINE void Vector::StoreFloat3(Float3& value) const
976
+ {
977
+ auto yVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1));
978
+ auto zVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(2, 2, 2, 2));
979
+ _mm_store_ss(&value.m_x, m_data);
980
+ _mm_store_ss(&value.m_y, yVec);
981
+ _mm_store_ss(&value.m_z, zVec);
982
+ }
983
+
984
+ FORCE_INLINE void Vector::StoreFloat4(Float4& value) const
985
+ {
986
+ _mm_storeu_ps(&value.m_x, m_data);
987
+ }
988
+
989
+ FORCE_INLINE float Vector::ToFloat() const
990
+ {
991
+ float v;
992
+ StoreFloat(v);
993
+ return v;
994
+ }
995
+
996
+ FORCE_INLINE Float2 Vector::ToFloat2() const
997
+ {
998
+ Float2 v;
999
+ StoreFloat2(v);
1000
+ return v;
1001
+ }
1002
+
1003
+ FORCE_INLINE Float3 Vector::ToFloat3() const
1004
+ {
1005
+ Float3 v;
1006
+ StoreFloat3(v);
1007
+ return v;
1008
+ }
1009
+
1010
+ FORCE_INLINE Float4 Vector::ToFloat4() const
1011
+ {
1012
+ Float4 v;
1013
+ StoreFloat4(v);
1014
+ return v;
1015
+ }
1016
+
1017
+ FORCE_INLINE Vector::operator Float2() const
1018
+ {
1019
+ return ToFloat2();
1020
+ }
1021
+
1022
+ FORCE_INLINE Vector::operator Float3() const
1023
+ {
1024
+ return ToFloat3();
1025
+ }
1026
+
1027
+ FORCE_INLINE Vector::operator Float4() const
1028
+ {
1029
+ return ToFloat4();
1030
+ }
1031
+
1032
+ FORCE_INLINE float Vector::GetX() const
1033
+ {
1034
+ return _mm_cvtss_f32(m_data);
1035
+ }
1036
+
1037
+ FORCE_INLINE float Vector::GetY() const
1038
+ {
1039
+ auto vTemp = GetSplatY();
1040
+ return _mm_cvtss_f32(vTemp);
1041
+ }
1042
+
1043
+ FORCE_INLINE float Vector::GetZ() const
1044
+ {
1045
+ auto vTemp = GetSplatZ();
1046
+ return _mm_cvtss_f32(vTemp);
1047
+ }
1048
+
1049
+ FORCE_INLINE float Vector::GetW() const
1050
+ {
1051
+ auto vTemp = GetSplatW();
1052
+ return _mm_cvtss_f32(vTemp);
1053
+ }
1054
+
1055
+ FORCE_INLINE void Vector::SetX(float x)
1056
+ {
1057
+ m_data = _mm_move_ss(m_data, _mm_set_ss(x));
1058
+ }
1059
+
1060
+ FORCE_INLINE void Vector::SetY(float y)
1061
+ {
1062
+ m_data = _mm_insert_ps(m_data, _mm_set_ss(y), 0x10);
1063
+ }
1064
+
1065
+ FORCE_INLINE void Vector::SetZ(float z)
1066
+ {
1067
+ m_data = _mm_insert_ps(m_data, _mm_set_ss(z), 0x20);
1068
+ }
1069
+
1070
+ FORCE_INLINE void Vector::SetW(float w)
1071
+ {
1072
+ m_data = _mm_insert_ps(m_data, _mm_set_ss(w), 0x30);
1073
+ }
1074
+
1075
+ FORCE_INLINE float Vector::operator[](uint32_t i) const
1076
+ {
1077
+ ASSERT(i < 4);
1078
+
1079
+ switch (i)
1080
+ {
1081
+ case 0: return GetX(); break;
1082
+ case 1: return GetY(); break;
1083
+ case 2: return GetZ(); break;
1084
+ case 3: return GetW(); break;
1085
+ }
1086
+
1087
+ UNREACHABLE_CODE();
1088
+ return 0.0f;
1089
+ }
1090
+
1091
+ FORCE_INLINE bool Vector::IsW1() const
1092
+ {
1093
+ return GetSplatW().IsEqual4(Vector::One);
1094
+ }
1095
+
1096
+ FORCE_INLINE bool Vector::IsW0() const
1097
+ {
1098
+ return GetSplatW().IsZero4();
1099
+ }
1100
+
1101
+ FORCE_INLINE Vector& Vector::SetW0()
1102
+ {
1103
+ SetW(0.0f);
1104
+ return *this;
1105
+ }
1106
+
1107
+ FORCE_INLINE Vector& Vector::SetW1()
1108
+ {
1109
+ SetW(1.0f);
1110
+ return *this;
1111
+ }
1112
+
1113
+ FORCE_INLINE Vector Vector::GetWithW0() const
1114
+ {
1115
+ Vector v = *this;
1116
+ v.SetW0();
1117
+ return v;
1118
+ }
1119
+
1120
+ FORCE_INLINE Vector Vector::GetWithW1() const
1121
+ {
1122
+ Vector v = *this;
1123
+ v.SetW1();
1124
+ return v;
1125
+ }
1126
+
1127
+ FORCE_INLINE Vector Vector::Get2D() const
1128
+ {
1129
+ return Vector::Select(*this, Vector::Zero, Vector::Select0011);
1130
+ }
1131
+
1132
+ FORCE_INLINE Vector Vector::Get3D() const
1133
+ {
1134
+ return Vector::Select(*this, Vector::Zero, Vector::Select0001);
1135
+ }
1136
+
1137
+ FORCE_INLINE Vector Vector::operator+(const Vector& v) const
1138
+ {
1139
+ return _mm_add_ps(m_data, v);
1140
+ }
1141
+
1142
+ FORCE_INLINE Vector& Vector::operator+=(const Vector& v)
1143
+ {
1144
+ m_data = _mm_add_ps(m_data, v);
1145
+ return *this;
1146
+ }
1147
+
1148
+ FORCE_INLINE Vector Vector::operator-(const Vector& v) const
1149
+ {
1150
+ return _mm_sub_ps(m_data, v);
1151
+ }
1152
+
1153
+ FORCE_INLINE Vector& Vector::operator-=(const Vector& v)
1154
+ {
1155
+ m_data = _mm_sub_ps(m_data, v);
1156
+ return *this;
1157
+ }
1158
+
1159
+ FORCE_INLINE Vector Vector::operator*(const Vector& v) const
1160
+ {
1161
+ return _mm_mul_ps(m_data, v);
1162
+ }
1163
+
1164
+ FORCE_INLINE Vector& Vector::operator*=(const Vector& v)
1165
+ {
1166
+ m_data = _mm_mul_ps(m_data, v);
1167
+ return *this;
1168
+ }
1169
+
1170
+ FORCE_INLINE Vector Vector::operator/(const Vector& v) const
1171
+ {
1172
+ return _mm_div_ps(m_data, v);
1173
+ }
1174
+
1175
+ FORCE_INLINE Vector& Vector::operator/=(const Vector& v)
1176
+ {
1177
+ m_data = _mm_div_ps(m_data, v);
1178
+ return *this;
1179
+ }
1180
+
1181
+ FORCE_INLINE Vector Vector::operator*(float const f) const
1182
+ {
1183
+ return operator*(Vector(f));
1184
+ }
1185
+
1186
+ FORCE_INLINE Vector& Vector::operator*=(float const f)
1187
+ {
1188
+ return operator*=(Vector(f));
1189
+ }
1190
+
1191
+ FORCE_INLINE Vector Vector::operator/(float const f) const
1192
+ {
1193
+ return operator/(Vector(f));
1194
+ }
1195
+
1196
+ FORCE_INLINE Vector& Vector::operator/=(float const f)
1197
+ {
1198
+ return operator/=(Vector(f));
1199
+ }
1200
+
1201
+ FORCE_INLINE Vector Vector::operator-() const
1202
+ {
1203
+ return GetNegated();
1204
+ }
1205
+
1206
+ FORCE_INLINE Vector Vector::Orthogonal2D() const
1207
+ {
1208
+ static Vector const negX(-1.0f, 1.0f, 1.0f, 1.0f);
1209
+
1210
+ Vector result;
1211
+ result = _mm_shuffle_ps(*this, *this, _MM_SHUFFLE(3, 2, 0, 1));
1212
+ result = _mm_mul_ps(result, negX);
1213
+ return result;
1214
+ }
1215
+
1216
+ FORCE_INLINE Vector Vector::Cross2(const Vector& other) const
1217
+ {
1218
+ Vector vResult = _mm_shuffle_ps(other.m_data, other.m_data, _MM_SHUFFLE(0, 1, 0, 1));
1219
+ vResult = _mm_mul_ps(vResult, m_data);
1220
+ Vector vTemp = vResult.GetSplatY();
1221
+ vResult = _mm_sub_ss(vResult, vTemp);
1222
+ vResult = vResult.GetSplatX();
1223
+ return vResult;
1224
+ }
1225
+
1226
+ FORCE_INLINE Vector Vector::Cross3(const Vector& other) const
1227
+ {
1228
+ auto vTemp1 = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(3, 0, 2, 1));
1229
+ auto vTemp2 = _mm_shuffle_ps(other, other, _MM_SHUFFLE(3, 1, 0, 2));
1230
+ Vector result = _mm_mul_ps(vTemp1, vTemp2);
1231
+ vTemp1 = _mm_shuffle_ps(vTemp1, vTemp1, _MM_SHUFFLE(3, 0, 2, 1));
1232
+ vTemp2 = _mm_shuffle_ps(vTemp2, vTemp2, _MM_SHUFFLE(3, 1, 0, 2));
1233
+ vTemp1 = _mm_mul_ps(vTemp1, vTemp2);
1234
+ result = _mm_sub_ps(result, vTemp1);
1235
+ result = _mm_and_ps(result, SIMD::g_maskXYZ0);
1236
+ return result;
1237
+ }
1238
+
1239
+ FORCE_INLINE Vector Vector::Dot2(const Vector& other) const
1240
+ {
1241
+ // Perform the dot product on m_x and m_y
1242
+ Vector result = _mm_mul_ps(m_data, other);
1243
+ // vTemp has m_y splatted
1244
+ auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 1, 1, 1));
1245
+ // m_x+m_y
1246
+ result = _mm_add_ss(result, vTemp);
1247
+ result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0));
1248
+ return result;
1249
+ }
1250
+
1251
+ FORCE_INLINE Vector Vector::Dot3(const Vector& vOther) const
1252
+ {
1253
+ // Perform the dot product
1254
+ auto vDot = _mm_mul_ps(m_data, vOther);
1255
+ // m_x=Dot.vector4_f32[1], m_y=Dot.vector4_f32[2]
1256
+ auto vTemp = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(2, 1, 2, 1));
1257
+ // Result.vector4_f32[0] = m_x+m_y
1258
+ vDot = _mm_add_ss(vDot, vTemp);
1259
+ // m_x=Dot.vector4_f32[2]
1260
+ vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1));
1261
+ // Result.vector4_f32[0] = (m_x+m_y)+m_z
1262
+ vDot = _mm_add_ss(vDot, vTemp);
1263
+ // Splat m_x
1264
+ Vector result = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(0, 0, 0, 0));
1265
+ return result;
1266
+ }
1267
+
1268
+ FORCE_INLINE Vector Vector::Dot4(const Vector& other) const
1269
+ {
1270
+ auto vTemp2 = other;
1271
+ auto vTemp = _mm_mul_ps(m_data, vTemp2);
1272
+ vTemp2 = _mm_shuffle_ps(vTemp2, vTemp, _MM_SHUFFLE(1, 0, 0, 0)); // Copy X to the Z position and Y to the W position
1273
+ vTemp2 = _mm_add_ps(vTemp2, vTemp); // Add Z = X+Z; W = Y+W;
1274
+ vTemp = _mm_shuffle_ps(vTemp, vTemp2, _MM_SHUFFLE(0, 3, 0, 0)); // Copy W to the Z position
1275
+ vTemp = _mm_add_ps(vTemp, vTemp2); // Add Z and W together
1276
+ return _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(2, 2, 2, 2)); // Splat Z and return
1277
+ }
1278
+
1279
+ FORCE_INLINE float Vector::GetDot2(const Vector& other) const
1280
+ {
1281
+ return Dot2(other).ToFloat();
1282
+ }
1283
+
1284
+ FORCE_INLINE float Vector::GetDot3(const Vector& other) const
1285
+ {
1286
+ return Dot3(other).ToFloat();
1287
+ }
1288
+
1289
+ FORCE_INLINE float Vector::GetDot4(const Vector& other) const
1290
+ {
1291
+ return Dot4(other).ToFloat();
1292
+ }
1293
+
1294
+ FORCE_INLINE Vector Vector::ScalarProjection(const Vector& other) const
1295
+ {
1296
+ Vector const normalizedThis = GetNormalized3();
1297
+ Vector const projection = other.Dot3(normalizedThis);
1298
+ return projection;
1299
+ }
1300
+
1301
+ FORCE_INLINE float Vector::GetScalarProjection(const Vector& other) const
1302
+ {
1303
+ return ScalarProjection(other).ToFloat();
1304
+ }
1305
+
1306
+ FORCE_INLINE Vector Vector::VectorProjection(const Vector& other) const
1307
+ {
1308
+ Vector const normalizedThis = GetNormalized3();
1309
+ Vector const dotOther = other.Dot3(normalizedThis);
1310
+ Vector const projection = normalizedThis * dotOther;
1311
+ return projection;
1312
+ }
1313
+
1314
+ FORCE_INLINE Vector& Vector::Invert()
1315
+ {
1316
+ m_data = _mm_div_ps(Vector::One, m_data);
1317
+ return *this;
1318
+ }
1319
+
1320
+ FORCE_INLINE Vector Vector::GetInverse() const
1321
+ {
1322
+ return _mm_div_ps(Vector::One, m_data);
1323
+ }
1324
+
1325
+ FORCE_INLINE Vector Vector::GetReciprocal() const
1326
+ {
1327
+ return GetInverse();
1328
+ }
1329
+
1330
+ FORCE_INLINE Vector& Vector::InvertEst()
1331
+ {
1332
+ m_data = _mm_rcp_ps(m_data);
1333
+ return *this;
1334
+ }
1335
+
1336
+ FORCE_INLINE Vector Vector::GetInverseEst() const
1337
+ {
1338
+ return _mm_rcp_ps(m_data);
1339
+ }
1340
+
1341
+ FORCE_INLINE Vector& Vector::Negate()
1342
+ {
1343
+ m_data = _mm_sub_ps(Vector::Zero, m_data);
1344
+ return *this;
1345
+ }
1346
+
1347
+ FORCE_INLINE Vector Vector::GetNegated() const
1348
+ {
1349
+ return _mm_sub_ps(Vector::Zero, m_data);
1350
+ }
1351
+
1352
+ FORCE_INLINE Vector& Vector::Abs()
1353
+ {
1354
+ m_data = _mm_max_ps(_mm_sub_ps(Vector::Zero, m_data), m_data);
1355
+ return *this;
1356
+ }
1357
+
1358
+ FORCE_INLINE Vector Vector::GetAbs() const
1359
+ {
1360
+ return _mm_max_ps(_mm_sub_ps(Vector::Zero, m_data), m_data);
1361
+ }
1362
+
1363
+ FORCE_INLINE Vector& Vector::Sqrt()
1364
+ {
1365
+ m_data = _mm_sqrt_ps(m_data);
1366
+ return *this;
1367
+ }
1368
+
1369
+ FORCE_INLINE Vector Vector::GetSqrt()
1370
+ {
1371
+ return _mm_sqrt_ps(m_data);
1372
+ }
1373
+
1374
+ FORCE_INLINE Vector& Vector::ReciprocalSqrt()
1375
+ {
1376
+ m_data = _mm_div_ps(Vector::One, _mm_sqrt_ps(m_data));
1377
+ return *this;
1378
+ }
1379
+
1380
+ FORCE_INLINE Vector Vector::GetReciprocalSqrt()
1381
+ {
1382
+ return _mm_div_ps(Vector::One, _mm_sqrt_ps(m_data));
1383
+ }
1384
+
1385
+ FORCE_INLINE Vector& Vector::EstimatedReciprocalSqrt()
1386
+ {
1387
+ m_data = _mm_rsqrt_ps(m_data);
1388
+ return *this;
1389
+ }
1390
+
1391
+ FORCE_INLINE Vector Vector::GetEstimatedReciprocalSqrt()
1392
+ {
1393
+ return _mm_rsqrt_ps(m_data);
1394
+ }
1395
+
1396
+ FORCE_INLINE Vector& Vector::Normalize2()
1397
+ {
1398
+ // Perform the dot product on m_x and m_y only
1399
+ auto vLengthSq = _mm_mul_ps(m_data, m_data);
1400
+ auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 1, 1, 1));
1401
+ vLengthSq = _mm_add_ss(vLengthSq, vTemp);
1402
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0));
1403
+ // Prepare for the division
1404
+ auto vResult = _mm_sqrt_ps(vLengthSq);
1405
+ // Create zero with a single instruction
1406
+ auto vZeroMask = _mm_setzero_ps();
1407
+ // Test for a divide by zero (Must be FP to detect -0.0)
1408
+ vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult);
1409
+ // Failsafe on zero (Or epsilon) length planes
1410
+ // If the length is infinity, set the elements to zero
1411
+ vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity);
1412
+ // Divide to perform the normalization
1413
+ vResult = _mm_div_ps(m_data, vResult);
1414
+ // Any that are infinity, set to zero
1415
+ vResult = _mm_and_ps(vResult, vZeroMask);
1416
+ // Select qnan or result based on infinite length
1417
+ auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN);
1418
+ auto vTemp2 = _mm_and_ps(vResult, vLengthSq);
1419
+ m_data = _mm_or_ps(vTemp1, vTemp2);
1420
+
1421
+ *this = Select(*this, Vector::Zero, Select0011);
1422
+
1423
+ return *this;
1424
+ }
1425
+
1426
+ FORCE_INLINE Vector& Vector::Normalize3()
1427
+ {
1428
+ // Perform the dot product on m_x,m_y and m_z only
1429
+ auto vLengthSq = _mm_mul_ps(m_data, m_data);
1430
+ auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 1, 2, 1));
1431
+ vLengthSq = _mm_add_ss(vLengthSq, vTemp);
1432
+ vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1));
1433
+ vLengthSq = _mm_add_ss(vLengthSq, vTemp);
1434
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0));
1435
+ // Prepare for the division
1436
+ auto vResult = _mm_sqrt_ps(vLengthSq);
1437
+ // Create zero with a single instruction
1438
+ auto vZeroMask = _mm_setzero_ps();
1439
+ // Test for a divide by zero (Must be FP to detect -0.0)
1440
+ vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult);
1441
+ // Failsafe on zero (Or epsilon) length planes
1442
+ // If the length is infinity, set the elements to zero
1443
+ vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity);
1444
+ // Divide to perform the normalization
1445
+ vResult = _mm_div_ps(m_data, vResult);
1446
+ // Any that are infinity, set to zero
1447
+ vResult = _mm_and_ps(vResult, vZeroMask);
1448
+ // Select qnan or result based on infinite length
1449
+ auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN);
1450
+ auto vTemp2 = _mm_and_ps(vResult, vLengthSq);
1451
+ m_data = _mm_or_ps(vTemp1, vTemp2);
1452
+
1453
+ *this = Select(*this, Vector::Zero, Select0001);
1454
+
1455
+ return *this;
1456
+ }
1457
+
1458
+ FORCE_INLINE Vector& Vector::Normalize4()
1459
+ {
1460
+ // Perform the dot product on m_x,m_y,m_z and m_w
1461
+ auto vLengthSq = _mm_mul_ps(m_data, m_data);
1462
+ // vTemp has m_z and m_w
1463
+ auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(3, 2, 3, 2));
1464
+ // m_x+m_z, m_y+m_w
1465
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
1466
+ // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w
1467
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 0, 0, 0));
1468
+ // ??,??,m_y+m_w,m_y+m_w
1469
+ vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0));
1470
+ // ??,??,m_x+m_z+m_y+m_w,??
1471
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
1472
+ // Splat the length
1473
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 2, 2, 2));
1474
+ // Prepare for the division
1475
+ auto vResult = _mm_sqrt_ps(vLengthSq);
1476
+ // Create zero with a single instruction
1477
+ auto vZeroMask = _mm_setzero_ps();
1478
+ // Test for a divide by zero (Must be FP to detect -0.0)
1479
+ vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult);
1480
+ // Failsafe on zero (Or epsilon) length planes
1481
+ // If the length is infinity, set the elements to zero
1482
+ vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity);
1483
+ // Divide to perform the normalization
1484
+ vResult = _mm_div_ps(m_data, vResult);
1485
+ // Any that are infinity, set to zero
1486
+ vResult = _mm_and_ps(vResult, vZeroMask);
1487
+ // Select qnan or result based on infinite length
1488
+ auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN);
1489
+ auto vTemp2 = _mm_and_ps(vResult, vLengthSq);
1490
+ m_data = _mm_or_ps(vTemp1, vTemp2);
1491
+
1492
+ return *this;
1493
+ }
1494
+
1495
+ FORCE_INLINE Vector Vector::GetNormalized2() const
1496
+ {
1497
+ Vector v = *this;
1498
+ v.Normalize2();
1499
+ return v;
1500
+ }
1501
+
1502
+ FORCE_INLINE Vector Vector::GetNormalized3() const
1503
+ {
1504
+ Vector v = *this;
1505
+ v.Normalize3();
1506
+ return v;
1507
+ }
1508
+
1509
+ FORCE_INLINE Vector Vector::GetNormalized4() const
1510
+ {
1511
+ Vector v = *this;
1512
+ v.Normalize4();
1513
+ return v;
1514
+ }
1515
+
1516
+ FORCE_INLINE Vector& Vector::Floor()
1517
+ {
1518
+ Vector result;
1519
+
1520
+ // To handle NAN, INF and numbers greater than 8388608, use masking
1521
+ __m128i vTest = _mm_and_si128(_mm_castps_si128(m_data), SIMD::g_absMask);
1522
+ vTest = _mm_cmplt_epi32(vTest, SIMD::g_noFraction);
1523
+ // Truncate
1524
+ __m128i vInt = _mm_cvttps_epi32(m_data);
1525
+ result = _mm_cvtepi32_ps(vInt);
1526
+ __m128 vLarger = _mm_cmpgt_ps(result, m_data);
1527
+ // 0 -> 0, 0xffffffff -> -1.0f
1528
+ vLarger = _mm_cvtepi32_ps(_mm_castps_si128(vLarger));
1529
+ result = _mm_add_ps(result, vLarger);
1530
+ // All numbers less than 8388608 will use the round to int
1531
+ result = _mm_and_ps(result, _mm_castsi128_ps(vTest));
1532
+ // All others, use the ORIGINAL value
1533
+ vTest = _mm_andnot_si128(vTest, _mm_castps_si128(m_data));
1534
+ result = _mm_or_ps(result, _mm_castsi128_ps(vTest));
1535
+
1536
+ m_data = result;
1537
+ return *this;
1538
+ }
1539
+
1540
+ FORCE_INLINE Vector Vector::GetFloor() const
1541
+ {
1542
+ Vector v = *this;
1543
+ v.Floor();
1544
+ return v;
1545
+ }
1546
+
1547
+ FORCE_INLINE Vector& Vector::Ceil()
1548
+ {
1549
+ Vector result;
1550
+
1551
+ // To handle NAN, INF and numbers greater than 8388608, use masking
1552
+ __m128i vTest = _mm_and_si128(_mm_castps_si128(m_data), SIMD::g_absMask);
1553
+ vTest = _mm_cmplt_epi32(vTest, SIMD::g_noFraction);
1554
+ // Truncate
1555
+ __m128i vInt = _mm_cvttps_epi32(m_data);
1556
+ result = _mm_cvtepi32_ps(vInt);
1557
+ __m128 vSmaller = _mm_cmplt_ps(result, m_data);
1558
+ // 0 -> 0, 0xffffffff -> -1.0f
1559
+ vSmaller = _mm_cvtepi32_ps(_mm_castps_si128(vSmaller));
1560
+ result = _mm_sub_ps(result, vSmaller);
1561
+ // All numbers less than 8388608 will use the round to int
1562
+ result = _mm_and_ps(result, _mm_castsi128_ps(vTest));
1563
+ // All others, use the ORIGINAL value
1564
+ vTest = _mm_andnot_si128(vTest, _mm_castps_si128(m_data));
1565
+ result = _mm_or_ps(result, _mm_castsi128_ps(vTest));
1566
+
1567
+ m_data = result;
1568
+ return *this;
1569
+ }
1570
+
1571
+ FORCE_INLINE Vector Vector::GetCeil() const
1572
+ {
1573
+ Vector v = *this;
1574
+ v.Ceil();
1575
+ return v;
1576
+ }
1577
+
1578
+ FORCE_INLINE Vector& Vector::Round()
1579
+ {
1580
+ __m128 sign = _mm_and_ps(m_data, SIMD::g_signMask);
1581
+ __m128 sMagic = _mm_or_ps(SIMD::g_noFraction, sign);
1582
+ __m128 R1 = _mm_add_ps(m_data, sMagic);
1583
+ R1 = _mm_sub_ps(R1, sMagic);
1584
+ __m128 R2 = _mm_and_ps(m_data, SIMD::g_absMask);
1585
+ __m128 mask = _mm_cmple_ps(R2, SIMD::g_noFraction);
1586
+ R2 = _mm_andnot_ps(mask, m_data);
1587
+ R1 = _mm_and_ps(R1, mask);
1588
+ m_data = _mm_xor_ps(R1, R2);
1589
+ return *this;
1590
+ }
1591
+
1592
+ FORCE_INLINE Vector Vector::GetRound() const
1593
+ {
1594
+ Vector v = *this;
1595
+ v.Round();
1596
+ return v;
1597
+ }
1598
+
1599
+ FORCE_INLINE Vector Vector::GetSign() const
1600
+ {
1601
+ Vector const selectMask = GreaterThanEqual(Vector::Zero);
1602
+ return Vector::Select(Vector::NegativeOne, Vector::One, selectMask);
1603
+ }
1604
+
1605
+ FORCE_INLINE Vector Vector::GetSplatX() const
1606
+ {
1607
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(0, 0, 0, 0));
1608
+ }
1609
+
1610
+ FORCE_INLINE Vector Vector::GetSplatY() const
1611
+ {
1612
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1));
1613
+ }
1614
+
1615
+ FORCE_INLINE Vector Vector::GetSplatZ() const
1616
+ {
1617
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(2, 2, 2, 2));
1618
+ }
1619
+
1620
+ FORCE_INLINE Vector Vector::GetSplatW() const
1621
+ {
1622
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(3, 3, 3, 3));
1623
+ }
1624
+
1625
+ template<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
1626
+ FORCE_INLINE Vector Vector::Swizzle() const
1627
+ {
1628
+ static_assert(xIdx < 4, "Element index parameter out of range");
1629
+ static_assert(yIdx < 4, "Element index parameter out of range");
1630
+ static_assert(zIdx < 4, "Element index parameter out of range");
1631
+ static_assert(wIdx < 4, "Element index parameter out of range");
1632
+ return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(wIdx, zIdx, yIdx, xIdx));
1633
+ }
1634
+
1635
+ FORCE_INLINE Vector Vector::Swizzle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const
1636
+ {
1637
+ ASSERT(xIdx < 4 && yIdx < 4 && zIdx < 4 && wIdx < 4);
1638
+ uint32_t const elem[4] = { xIdx, yIdx, zIdx, wIdx };
1639
+ __m128i vControl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&elem[0]));
1640
+ return _mm_permutevar_ps(m_data, vControl);
1641
+ }
1642
+
1643
+ FORCE_INLINE Vector Vector::Shuffle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const
1644
+ {
1645
+ return Swizzle(xIdx, yIdx, zIdx, wIdx);
1646
+ }
1647
+
1648
+ template<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
1649
+ FORCE_INLINE Vector Vector::Shuffle() const
1650
+ {
1651
+ return Swizzle<xIdx, yIdx, zIdx, wIdx>();
1652
+ }
1653
+
1654
+ FORCE_INLINE Vector Vector::Length2() const
1655
+ {
1656
+ Vector result;
1657
+
1658
+ result = _mm_mul_ps(m_data, m_data);
1659
+ auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 1, 1, 1));
1660
+ // m_x+m_y
1661
+ result = _mm_add_ss(result, vTemp);
1662
+ result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0));
1663
+ result = _mm_sqrt_ps(result);
1664
+ return result;
1665
+ }
1666
+
1667
+ FORCE_INLINE Vector Vector::Length3() const
1668
+ {
1669
+ Vector result;
1670
+
1671
+ // Perform the dot product on m_x,m_y and m_z
1672
+ result = _mm_mul_ps(m_data, m_data);
1673
+ // vTemp has m_z and m_y
1674
+ auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 2, 1, 2));
1675
+ // m_x+m_z, m_y
1676
+ result = _mm_add_ss(result, vTemp);
1677
+ // m_y,m_y,m_y,m_y
1678
+ vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1));
1679
+ // m_x+m_z+m_y,??,??,??
1680
+ result = _mm_add_ss(result, vTemp);
1681
+ // Splat the length squared
1682
+ result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0));
1683
+ // Get the length
1684
+ result = _mm_sqrt_ps(result);
1685
+
1686
+ return result;
1687
+ }
1688
+
1689
+ FORCE_INLINE Vector Vector::Length4() const
1690
+ {
1691
+ Vector result;
1692
+
1693
+ // Perform the dot product on m_x,m_y,m_z and m_w
1694
+ result = _mm_mul_ps(m_data, m_data);
1695
+ // vTemp has m_z and m_w
1696
+ auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(3, 2, 3, 2));
1697
+ // m_x+m_z, m_y+m_w
1698
+ result = _mm_add_ps(result, vTemp);
1699
+ // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w
1700
+ result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 0, 0, 0));
1701
+ // ??,??,m_y+m_w,m_y+m_w
1702
+ vTemp = _mm_shuffle_ps(vTemp, result, _MM_SHUFFLE(3, 3, 0, 0));
1703
+ // ??,??,m_x+m_z+m_y+m_w,??
1704
+ result = _mm_add_ps(result, vTemp);
1705
+ // Splat the length
1706
+ result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(2, 2, 2, 2));
1707
+ // Get the length
1708
+ result = _mm_sqrt_ps(result);
1709
+
1710
+ return result;
1711
+ }
1712
+
1713
+ FORCE_INLINE float Vector::GetLength2() const
1714
+ {
1715
+ return Length2().GetX();
1716
+ }
1717
+
1718
+ FORCE_INLINE float Vector::GetLength3() const
1719
+ {
1720
+ return Length3().GetX();
1721
+ }
1722
+
1723
+ FORCE_INLINE float Vector::GetLength4() const
1724
+ {
1725
+ return Length4().GetX();
1726
+ }
1727
+
1728
+ FORCE_INLINE Vector Vector::InverseLength2() const
1729
+ {
1730
+ // Perform the dot product on m_x and m_y
1731
+ auto vLengthSq = _mm_mul_ps(m_data, m_data);
1732
+ // vTemp has m_y splatted
1733
+ auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 1, 1, 1));
1734
+ // m_x+m_y
1735
+ vLengthSq = _mm_add_ss(vLengthSq, vTemp);
1736
+ vLengthSq = _mm_sqrt_ss(vLengthSq);
1737
+ vLengthSq = _mm_div_ss(Vector::One, vLengthSq);
1738
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0));
1739
+ return vLengthSq;
1740
+ }
1741
+
1742
+ FORCE_INLINE Vector Vector::InverseLength3() const
1743
+ {
1744
+ // Perform the dot product
1745
+ auto vDot = _mm_mul_ps(m_data, m_data);
1746
+ // m_x=Dot.m_y, m_y=Dot.m_z
1747
+ auto vTemp = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(2, 1, 2, 1));
1748
+ // Result.m_x = m_x+m_y
1749
+ vDot = _mm_add_ss(vDot, vTemp);
1750
+ // m_x=Dot.m_z
1751
+ vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1));
1752
+ // Result.m_x = (m_x+m_y)+m_z
1753
+ vDot = _mm_add_ss(vDot, vTemp);
1754
+ // Splat m_x
1755
+ vDot = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(0, 0, 0, 0));
1756
+ // Get the reciprocal
1757
+ vDot = _mm_sqrt_ps(vDot);
1758
+ // Get the reciprocal
1759
+ vDot = _mm_div_ps(Vector::One, vDot);
1760
+ return vDot;
1761
+ }
1762
+
1763
+ FORCE_INLINE Vector Vector::InverseLength4() const
1764
+ {
1765
+ // Perform the dot product on m_x,m_y,m_z and m_w
1766
+ auto vLengthSq = _mm_mul_ps(m_data, m_data);
1767
+ // vTemp has m_z and m_w
1768
+ auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(3, 2, 3, 2));
1769
+ // m_x+m_z, m_y+m_w
1770
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
1771
+ // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w
1772
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 0, 0, 0));
1773
+ // ??,??,m_y+m_w,m_y+m_w
1774
+ vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0));
1775
+ // ??,??,m_x+m_z+m_y+m_w,??
1776
+ vLengthSq = _mm_add_ps(vLengthSq, vTemp);
1777
+ // Splat the length
1778
+ vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 2, 2, 2));
1779
+ // Get the reciprocal
1780
+ vLengthSq = _mm_sqrt_ps(vLengthSq);
1781
+ // Accurate!
1782
+ vLengthSq = _mm_div_ps(Vector::One, vLengthSq);
1783
+ return vLengthSq;
1784
+ }
1785
+
1786
+ FORCE_INLINE float Vector::GetInverseLength2() const
1787
+ {
1788
+ return InverseLength2().GetX();
1789
+ }
1790
+
1791
+ FORCE_INLINE float Vector::GetInverseLength3() const
1792
+ {
1793
+ return InverseLength3().GetX();
1794
+ }
1795
+
1796
+ FORCE_INLINE float Vector::GetInverseLength4() const
1797
+ {
1798
+ return InverseLength4().GetX();
1799
+ }
1800
+
1801
+ FORCE_INLINE Vector Vector::LengthSquared2() const
1802
+ {
1803
+ return Vector::Dot2(m_data, m_data);
1804
+ }
1805
+
1806
+ FORCE_INLINE Vector Vector::LengthSquared3() const
1807
+ {
1808
+ return Vector::Dot3(m_data, m_data);
1809
+ }
1810
+
1811
+ FORCE_INLINE Vector Vector::LengthSquared4() const
1812
+ {
1813
+ return Vector::Dot4(m_data, m_data);
1814
+ }
1815
+
1816
+ FORCE_INLINE float Vector::GetLengthSquared2() const
1817
+ {
1818
+ return LengthSquared2().GetX();
1819
+ }
1820
+
1821
+ FORCE_INLINE float Vector::GetLengthSquared3() const
1822
+ {
1823
+ return LengthSquared3().GetX();
1824
+ }
1825
+
1826
+ FORCE_INLINE float Vector::GetLengthSquared4() const
1827
+ {
1828
+ return LengthSquared4().GetX();
1829
+ }
1830
+
1831
+ FORCE_INLINE Vector Vector::Distance2(const Vector& to) const
1832
+ {
1833
+ return (to - *this).Length2();
1834
+ }
1835
+
1836
+ FORCE_INLINE Vector Vector::Distance3(const Vector& to) const
1837
+ {
1838
+ return (to - *this).Length3();
1839
+ }
1840
+
1841
+ FORCE_INLINE Vector Vector::Distance4(const Vector& to) const
1842
+ {
1843
+ return (to - *this).Length4();
1844
+ }
1845
+
1846
+ FORCE_INLINE float Vector::GetDistance2(const Vector& to) const
1847
+ {
1848
+ return (to - *this).Length2().GetX();
1849
+ }
1850
+
1851
+ FORCE_INLINE float Vector::GetDistance3(const Vector& to) const
1852
+ {
1853
+ return (to - *this).Length3().GetX();
1854
+ }
1855
+
1856
+ FORCE_INLINE float Vector::GetDistance4(const Vector& to) const
1857
+ {
1858
+ return (to - *this).Length4().GetX();
1859
+ }
1860
+
1861
+ FORCE_INLINE Vector Vector::DistanceSquared2(const Vector& to) const
1862
+ {
1863
+ return (to - *this).LengthSquared2();
1864
+ }
1865
+
1866
+ FORCE_INLINE Vector Vector::DistanceSquared3(const Vector& to) const
1867
+ {
1868
+ return (to - *this).LengthSquared3();
1869
+ }
1870
+
1871
+ FORCE_INLINE Vector Vector::DistanceSquared4(const Vector& to) const
1872
+ {
1873
+ return (to - *this).LengthSquared4();
1874
+ }
1875
+
1876
+ FORCE_INLINE float Vector::GetDistanceSquared2(const Vector& to) const
1877
+ {
1878
+ return (to - *this).GetLengthSquared2();
1879
+ }
1880
+
1881
+ FORCE_INLINE float Vector::GetDistanceSquared3(const Vector& to) const
1882
+ {
1883
+ return (to - *this).GetLengthSquared3();
1884
+ }
1885
+
1886
+ FORCE_INLINE float Vector::GetDistanceSquared4(const Vector& to) const
1887
+ {
1888
+ return (to - *this).GetLengthSquared4();
1889
+ }
1890
+
1891
+ FORCE_INLINE bool Vector::IsNormalized2() const
1892
+ {
1893
+ return (LengthSquared2() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold);
1894
+ }
1895
+
1896
+ FORCE_INLINE bool Vector::IsNormalized3() const
1897
+ {
1898
+ return (LengthSquared3() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold);
1899
+ }
1900
+
1901
+ FORCE_INLINE bool Vector::IsNormalized4() const
1902
+ {
1903
+ return (LengthSquared4() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold);
1904
+ }
1905
+
1906
+ FORCE_INLINE Vector Vector::InBounds(const Vector& bounds) const
1907
+ {
1908
+ // Test if less than or equal
1909
+ auto vTemp1 = _mm_cmple_ps(m_data, bounds);
1910
+ // Negate the bounds
1911
+ auto vTemp2 = _mm_mul_ps(bounds, Vector::NegativeOne);
1912
+ // Test if greater or equal (Reversed)
1913
+ vTemp2 = _mm_cmple_ps(vTemp2, m_data);
1914
+ // Blend answers
1915
+ vTemp1 = _mm_and_ps(vTemp1, vTemp2);
1916
+ return vTemp1;
1917
+ }
1918
+
1919
+ FORCE_INLINE bool Vector::IsInBounds2(const Vector& bounds) const
1920
+ {
1921
+ return ((_mm_movemask_ps(InBounds(bounds)) & 0x3) == 0x3) != 0;
1922
+ }
1923
+
1924
+ FORCE_INLINE bool Vector::IsInBounds3(const Vector& bounds) const
1925
+ {
1926
+ return ((_mm_movemask_ps(InBounds(bounds)) & 0x7) == 0x7) != 0;
1927
+ }
1928
+
1929
+ FORCE_INLINE bool Vector::IsInBounds4(const Vector& bounds) const
1930
+ {
1931
+ return (_mm_movemask_ps(InBounds(bounds)) == 0x0f) != 0;
1932
+ }
1933
+
1934
+ FORCE_INLINE Vector Vector::Equal(const Vector& v) const
1935
+ {
1936
+ return _mm_cmpeq_ps(*this, v);
1937
+ }
1938
+
1939
+ FORCE_INLINE bool Vector::IsEqual2(const Vector& v) const
1940
+ {
1941
+ return (((_mm_movemask_ps(Equal(v)) & 3) == 3) != 0);
1942
+ }
1943
+
1944
+ FORCE_INLINE bool Vector::IsEqual3(const Vector& v) const
1945
+ {
1946
+ return (((_mm_movemask_ps(Equal(v)) & 7) == 7) != 0);
1947
+ }
1948
+
1949
+ FORCE_INLINE bool Vector::IsEqual4(const Vector& v) const
1950
+ {
1951
+ return ((_mm_movemask_ps(Equal(v)) == 0x0f) != 0);
1952
+ }
1953
+
1954
+ FORCE_INLINE Vector Vector::NearEqual(const Vector& v, const Vector& epsilon) const
1955
+ {
1956
+ // Get the difference
1957
+ auto vDelta = _mm_sub_ps(m_data, v);
1958
+ // Get the absolute value of the difference
1959
+ auto vTemp = _mm_setzero_ps();
1960
+ vTemp = _mm_sub_ps(vTemp, vDelta);
1961
+ vTemp = _mm_max_ps(vTemp, vDelta);
1962
+ vTemp = _mm_cmple_ps(vTemp, epsilon);
1963
+ return vTemp;
1964
+ }
1965
+
1966
+ FORCE_INLINE bool Vector::IsNearEqual2(const Vector& v, float epsilon) const
1967
+ {
1968
+ return IsNearEqual2(v, Vector(epsilon));
1969
+ }
1970
+
1971
+ FORCE_INLINE bool Vector::IsNearEqual3(const Vector& v, float epsilon) const
1972
+ {
1973
+ return IsNearEqual3(v, Vector(epsilon));
1974
+ }
1975
+
1976
+ FORCE_INLINE bool Vector::IsNearEqual4(const Vector& v, float epsilon) const
1977
+ {
1978
+ return IsNearEqual4(v, Vector(epsilon));
1979
+ }
1980
+
1981
+ FORCE_INLINE bool Vector::IsNearEqual2(const Vector& v, const Vector& epsilon) const
1982
+ {
1983
+ return (((_mm_movemask_ps(NearEqual(v, epsilon)) & 3) == 0x3) != 0);
1984
+ }
1985
+
1986
+ FORCE_INLINE bool Vector::IsNearEqual3(const Vector& v, const Vector& epsilon) const
1987
+ {
1988
+ return (((_mm_movemask_ps(NearEqual(v, epsilon)) & 7) == 0x7) != 0);
1989
+ }
1990
+
1991
+ FORCE_INLINE bool Vector::IsNearEqual4(const Vector& v, const Vector& epsilon) const
1992
+ {
1993
+ return ((_mm_movemask_ps(NearEqual(v, epsilon)) == 0xf) != 0);
1994
+ }
1995
+
1996
+ FORCE_INLINE Vector Vector::GreaterThan(const Vector& v) const
1997
+ {
1998
+ return _mm_cmpgt_ps(m_data, v);
1999
+ }
2000
+
2001
+ FORCE_INLINE bool Vector::IsAnyGreaterThan(const Vector& v) const
2002
+ {
2003
+ return !GreaterThan(v).IsZero4();
2004
+ }
2005
+
2006
+ FORCE_INLINE bool Vector::IsGreaterThan2(const Vector& v) const
2007
+ {
2008
+ return (((_mm_movemask_ps(GreaterThan(v)) & 3) == 3) != 0);
2009
+ }
2010
+
2011
+ FORCE_INLINE bool Vector::IsGreaterThan3(const Vector& v) const
2012
+ {
2013
+ return (((_mm_movemask_ps(GreaterThan(v)) & 7) == 7) != 0);
2014
+ }
2015
+
2016
+ FORCE_INLINE bool Vector::IsGreaterThan4(const Vector& v) const
2017
+ {
2018
+ return ((_mm_movemask_ps(GreaterThan(v)) == 0x0f) != 0);
2019
+ }
2020
+
2021
+ FORCE_INLINE Vector Vector::GreaterThanEqual(const Vector& v) const
2022
+ {
2023
+ return _mm_cmpge_ps(m_data, v);
2024
+ }
2025
+
2026
+ FORCE_INLINE bool Vector::IsAnyGreaterThanEqual(const Vector& v) const
2027
+ {
2028
+ return !GreaterThanEqual(v).IsZero4();
2029
+ }
2030
+
2031
+ FORCE_INLINE bool Vector::IsGreaterThanEqual2(const Vector& v) const
2032
+ {
2033
+ return ((_mm_movemask_ps(GreaterThanEqual(v)) & 3) == 3) != 0;
2034
+ }
2035
+
2036
+ FORCE_INLINE bool Vector::IsGreaterThanEqual3(const Vector& v) const
2037
+ {
2038
+ return ((_mm_movemask_ps(GreaterThanEqual(v)) & 7) == 7) != 0;
2039
+ }
2040
+
2041
+ FORCE_INLINE bool Vector::IsGreaterThanEqual4(const Vector& v) const
2042
+ {
2043
+ return (_mm_movemask_ps(GreaterThanEqual(v)) == 0x0f) != 0;
2044
+ }
2045
+
2046
+ FORCE_INLINE Vector Vector::LessThan(const Vector& v) const
2047
+ {
2048
+ return _mm_cmplt_ps(m_data, v);
2049
+ }
2050
+
2051
+ FORCE_INLINE bool Vector::IsAnyLessThan(const Vector& v) const
2052
+ {
2053
+ return !LessThan(v).IsZero4();
2054
+ }
2055
+
2056
+ FORCE_INLINE bool Vector::IsLessThan2(const Vector& v) const
2057
+ {
2058
+ return (((_mm_movemask_ps(LessThan(v)) & 3) == 3) != 0);
2059
+ }
2060
+
2061
+ FORCE_INLINE bool Vector::IsLessThan3(const Vector& v) const
2062
+ {
2063
+ return (((_mm_movemask_ps(LessThan(v)) & 7) == 7) != 0);
2064
+ }
2065
+
2066
+ FORCE_INLINE bool Vector::IsLessThan4(const Vector& v) const
2067
+ {
2068
+ return ((_mm_movemask_ps(LessThan(v)) == 0x0f) != 0);
2069
+ }
2070
+
2071
+ FORCE_INLINE Vector Vector::LessThanEqual(const Vector& v) const
2072
+ {
2073
+ return _mm_cmple_ps(m_data, v);
2074
+ }
2075
+
2076
+ FORCE_INLINE bool Vector::IsAnyLessThanEqual(const Vector& v) const
2077
+ {
2078
+ return !LessThanEqual(v).IsZero4();
2079
+ }
2080
+
2081
+ FORCE_INLINE bool Vector::IsLessThanEqual2(const Vector& v) const
2082
+ {
2083
+ return (((_mm_movemask_ps(LessThanEqual(v)) & 3) == 3) != 0);
2084
+ }
2085
+
2086
+ FORCE_INLINE bool Vector::IsLessThanEqual3(const Vector& v) const
2087
+ {
2088
+ return (((_mm_movemask_ps(LessThanEqual(v)) & 7) == 7) != 0);
2089
+ }
2090
+
2091
+ FORCE_INLINE bool Vector::IsLessThanEqual4(const Vector& v) const
2092
+ {
2093
+ return ((_mm_movemask_ps(LessThanEqual(v)) == 0x0f) != 0);
2094
+ }
2095
+
2096
+ FORCE_INLINE Vector Vector::EqualsZero() const
2097
+ {
2098
+ return Equal(Vector::Zero);
2099
+ }
2100
+
2101
+ FORCE_INLINE bool Vector::IsAnyEqualToZero2() const
2102
+ {
2103
+ return !EqualsZero().IsZero2();
2104
+ }
2105
+
2106
+ FORCE_INLINE bool Vector::IsAnyEqualToZero3() const
2107
+ {
2108
+ return !EqualsZero().IsZero3();
2109
+ }
2110
+
2111
+ FORCE_INLINE bool Vector::IsAnyEqualToZero4() const
2112
+ {
2113
+ return !EqualsZero().IsZero4();
2114
+ }
2115
+
2116
+ FORCE_INLINE bool Vector::IsZero2() const
2117
+ {
2118
+ return IsEqual2(Vector::Zero);
2119
+ }
2120
+
2121
+ FORCE_INLINE bool Vector::IsZero3() const
2122
+ {
2123
+ return IsEqual3(Vector::Zero);
2124
+ }
2125
+
2126
+ FORCE_INLINE bool Vector::IsZero4() const
2127
+ {
2128
+ return IsEqual4(Vector::Zero);
2129
+ }
2130
+
2131
+ FORCE_INLINE Vector Vector::NearEqualsZero(float epsilon) const
2132
+ {
2133
+ return NearEqual(Vector::Zero, Vector(epsilon));
2134
+ }
2135
+
2136
+ FORCE_INLINE bool Vector::IsNearZero2(float epsilon) const
2137
+ {
2138
+ return IsNearEqual2(Vector::Zero, Vector(epsilon));
2139
+ }
2140
+
2141
+ FORCE_INLINE bool Vector::IsNearZero3(float epsilon) const
2142
+ {
2143
+ return IsNearEqual3(Vector::Zero, Vector(epsilon));
2144
+ }
2145
+
2146
+ FORCE_INLINE bool Vector::IsNearZero4(float epsilon) const
2147
+ {
2148
+ return IsNearEqual4(Vector::Zero, Vector(epsilon));
2149
+ }
2150
+
2151
+ FORCE_INLINE Vector Vector::EqualsInfinity() const
2152
+ {
2153
+ __m128 vTemp = _mm_and_ps(m_data, SIMD::g_absMask);
2154
+ return _mm_cmpeq_ps(vTemp, Vector::Infinity);
2155
+ }
2156
+
2157
+ FORCE_INLINE bool Vector::IsInfinite2() const
2158
+ {
2159
+ return (_mm_movemask_ps(EqualsInfinity()) & 3) != 0;
2160
+ }
2161
+
2162
+ FORCE_INLINE bool Vector::IsInfinite3() const
2163
+ {
2164
+ return (_mm_movemask_ps(EqualsInfinity()) & 7) != 0;
2165
+ }
2166
+
2167
+ FORCE_INLINE bool Vector::IsInfinite4() const
2168
+ {
2169
+ return (_mm_movemask_ps(EqualsInfinity()) != 0);
2170
+ }
2171
+
2172
+ FORCE_INLINE Vector Vector::EqualsNaN() const
2173
+ {
2174
+ return _mm_cmpneq_ps(m_data, m_data);
2175
+ }
2176
+
2177
+ FORCE_INLINE bool Vector::IsNaN2() const
2178
+ {
2179
+ return (_mm_movemask_ps(EqualsNaN()) & 3) != 0;
2180
+ }
2181
+
2182
+ FORCE_INLINE bool Vector::IsNaN3() const
2183
+ {
2184
+ return (_mm_movemask_ps(EqualsNaN()) & 7) != 0;
2185
+ }
2186
+
2187
+ FORCE_INLINE bool Vector::IsNaN4() const
2188
+ {
2189
+ return (_mm_movemask_ps(EqualsNaN()) != 0);
2190
+ }
2191
+
2192
+ FORCE_INLINE bool Vector::IsParallelTo(const Vector& v) const
2193
+ {
2194
+ Vector const vAbsDot = Vector::Dot3(*this, v).GetAbs();
2195
+ Vector const vAbsDelta = Vector::One - vAbsDot;
2196
+ return vAbsDelta.IsLessThanEqual4(Vector::Epsilon);
2197
+ }
2198
+
2199
+ FORCE_INLINE void Vector::ToDirectionAndLength2(Vector& direction, float& length) const
2200
+ {
2201
+ Vector const vLength = Length2();
2202
+ direction = Vector::Select(*this, Vector::Zero, Select0011);
2203
+ direction /= vLength;
2204
+ length = vLength.ToFloat();
2205
+ }
2206
+
2207
+ FORCE_INLINE void Vector::ToDirectionAndLength3(Vector& direction, float& length) const
2208
+ {
2209
+ Vector const vLength = Length3();
2210
+ direction = Vector::Select(*this, Vector::Zero, Select0001);
2211
+ direction /= vLength;
2212
+ length = vLength.ToFloat();
2213
+ }
2214
+
2215
+ FORCE_INLINE bool Vector::operator==(const Vector& rhs) const
2216
+ {
2217
+ return IsEqual4(rhs);
2218
+ }
2219
+
2220
+ FORCE_INLINE bool Vector::operator!=(const Vector& rhs) const
2221
+ {
2222
+ return !IsEqual4(rhs);
2223
+ }
2224
+ }
MotionCorrection/src/cpp/Platform.h ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ #pragma once
7
+
8
+ // Finds the current platform
9
+ #if defined( __WIN32__ ) || defined( _WIN32 )
10
+ # define PLATFORM_WIN32
11
+ #else
12
+ # define PLATFORM_LINUX
13
+ #endif
14
+
15
+ //
16
+ // Platform Specific Helpers/Functions
17
+ //
18
+
19
+ // DLL export
20
+ #if defined(PLATFORM_WIN32) // Windows
21
+ # if defined(COMPILER_MSVC)
22
+ # if defined(STATIC_LIB)
23
+ # define API
24
+ # else
25
+ # if defined(API)
26
+ # define API __declspec(dllexport)
27
+ # else
28
+ # define API __declspec(dllimport)
29
+ # endif
30
+ # endif
31
+ # else
32
+ # if defined(STATIC_LIB)
33
+ # define API
34
+ # else
35
+ # if defined(API)
36
+ # define API __attribute__ ((dllexport))
37
+ # else
38
+ # define API __attribute__ ((dllimport))
39
+ # endif
40
+ # endif
41
+ # endif
42
+ # define DISABLE_OPTIMIZATION __pragma( optimize( "", off ) )
43
+ # define ENABLE_OPTIMIZATION __pragma( optimize( "", on ) )
44
+ # define DEBUG_BREAK() // __debugbreak()
45
+ #else // Linux settings
46
+ # include <signal.h>
47
+ # define API __attribute__ ((visibility ("default")))
48
+ # define DISABLE_OPTIMIZATION
49
+ # define ENABLE_OPTIMIZATION
50
+ # define DEBUG_BREAK() // raise(SIGTRAP)
51
+ #endif
README.md CHANGED
@@ -1,13 +1,278 @@
1
  ---
2
- title: Kimodo Motion Api
3
- emoji: 🏃
4
- colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.20.0
8
- python_version: '3.13'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Kimodo Motion API
3
+ emoji: 💃
4
+ colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.20.0
 
8
  app_file: app.py
9
+ python_version: "3.12"
10
+ startup_duration_timeout: 1h
11
+ short_description: Text-to-motion BVH generation for Blender
12
  ---
13
 
14
+ <p align="center">
15
+ <img src="./assets/banner.png" alt="Banner" width="100%">
16
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-76B900.svg" alt="License"></a>
17
+ <a href="https://research.nvidia.com/labs/sil/projects/kimodo/"><img src="https://img.shields.io/badge/Project-Page-blue" alt="Project Page"></a>
18
+ <a href="https://research.nvidia.com/labs/sil/projects/kimodo/docs/index.html"><img src="https://img.shields.io/badge/docs-online-green.svg" alt="Documentation"></a>
19
+ </p>
20
+
21
+ ## Overview
22
+
23
+ Kimodo is a **ki**nematic **mo**tion **d**iffusi**o**n model trained on a large-scale (700 hours) commercially-friendly optical motion capture dataset. The model generates high-quality 3D human and robot motions, and is controlled through text prompts and an extensive set of constraints such as full-body pose keyframes, end-effector positions/rotations, 2D paths, and 2D waypoints. Full details of the model architecture and training are available in the [technical report](https://research.nvidia.com/labs/sil/projects/kimodo/assets/kimodo_tech_report.pdf).
24
+
25
+ This repository provides:
26
+ - **Inference**: code and CLI to generate motions on both human and robot skeletons
27
+ - **Interactive Demo**: easily author motions with a timeline interface of text prompts and kinematic controls
28
+ - **Benchmark**: [test cases](https://huggingface.co/datasets/nvidia/Kimodo-Motion-Gen-Benchmark) and evaluation code built on the [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) dataset to evaluate motion generation models based on text and constraint-following abilities
29
+ - **Annotations**: fine-grained temporal text descriptions created for the Kimodo project are included in the [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) dataset. For more information on these labels, see our separate [Hugging Face repo](https://huggingface.co/datasets/nvidia/SEED-Timeline-Annotations).
30
+
31
+ ### ZeroGPU motion API
32
+
33
+ This fork includes a lightweight Hugging Face Gradio app in `app.py`. It exposes a
34
+ `/generate_motion` endpoint that generates one SOMA motion and returns Blender-compatible
35
+ BVH, Kimodo NPZ, and JSON metadata. The endpoint is designed for ZeroGPU and deliberately
36
+ disables the optional native motion-correction pass so the Space does not need to compile
37
+ the `MotionCorrection` extension.
38
+
39
+ Set the Space hardware to **ZeroGPU** and add an `HF_TOKEN` secret with access to the gated
40
+ Llama 3 repositories used by Kimodo's LLM2Vec text encoder. The default model is
41
+ `Kimodo-SOMA-RP-v1.1`; override it with the `KIMODO_MODEL` Space variable if needed.
42
+
43
+ <div align="center">
44
+ <img src="assets/teaser.gif" width="1280">
45
+ </div>
46
+
47
+ ## News
48
+
49
+ See the [full changelog](CHANGELOG.md) for a detailed list of all changes.
50
+
51
+ - **[2026-07-10]** Released the [ARDY project](https://research.nvidia.com/labs/sil/projects/ardy/) -- a _real-time_ motion generation model with all the controllability of Kimodo!
52
+ - **[2026-05-03]** _FIX_: fixed a bug causing incorrect calculation of averaged metrics for constraint test cases in the benchmark
53
+ - **[2026-04-24]** _NEW_: improved multi-prompt generation and better support for small VRAM GPUs via `TEXT_ENCODER_DEVICE=cpu` env var
54
+ - **[2026-04-10]** Released the [Kimodo Motion Generation Benchmark](#kimodo-motion-generation-benchmark) alongside new v1.1 Kimodo-SOMA models
55
+ - **[2026-03-19]** **Breaking:** Model inputs/outputs now use the SOMA 77-joint skeleton (`somaskel77`).
56
+ - **[2026-03-16]** Initial open-source release of Kimodo with five model variants (SOMA, G1, SMPL-X), CLI, interactive demo, and timeline annotations for BONES-SEED.
57
+
58
+
59
+ ## Kimodo Models
60
+
61
+ Several variations of Kimodo are available trained on various skeletons and datasets. All models support text-to-motion and kinematic controls.
62
+
63
+ > Note: models will be downloaded automatically when attempting to generate from the CLI or Interactive Demo, so there is no need to download them manually
64
+
65
+ | Model | Skeleton | Training Data | Release Date | Hugging Face | License |
66
+ |:-------|:-------------|:------:|:------:|:-------------:|:-------------:|
67
+ | **Kimodo-SOMA-RP-v1.1** | [SOMA](https://github.com/NVlabs/SOMA-X) | [Bones Rigplay 1](https://bones.studio/datasets#rp01) | April 10, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-SOMA-RP-v1.1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
68
+ | **Kimodo-SOMA-SEED-v1.1** | [SOMA](https://github.com/NVlabs/SOMA-X) | [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) | April 10, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-SOMA-SEED-v1.1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
69
+ | **Kimodo-SOMA-RP-v1** | [SOMA](https://github.com/NVlabs/SOMA-X) | [Bones Rigplay 1](https://bones.studio/datasets#rp01) | March 16, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-SOMA-RP-v1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
70
+ | **Kimodo-G1-RP-v1** | [Unitree G1](https://github.com/unitreerobotics/unitree_mujoco/tree/main/unitree_robots/g1) | [Bones Rigplay 1](https://bones.studio/datasets#rp01) | March 16, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-G1-RP-v1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
71
+ | **Kimodo-SOMA-SEED-v1** | [SOMA](https://github.com/NVlabs/SOMA-X) | [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) | March 16, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-SOMA-SEED-v1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
72
+ | **Kimodo-G1-SEED-v1** | [Unitree G1](https://github.com/unitreerobotics/unitree_mujoco/tree/main/unitree_robots/g1) | [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) | March 16, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-G1-SEED-v1) | [NVIDIA Open Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) |
73
+ | **Kimodo-SMPLX-RP-v1** | [SMPL-X](https://github.com/vchoutas/smplx) | [Bones Rigplay 1](https://bones.studio/datasets#rp01) | March 16, 2026 | [Link](https://huggingface.co/nvidia/Kimodo-SMPLX-RP-v1) | [NVIDIA R&D Model](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-internal-scientific-research-and-development-model-license/) |
74
+
75
+ By default, we recommend using the models trained on the full Bones Rigplay 1 dataset (700 hours of mocap) for your motion generation needs.
76
+ The models trained on BONES-SEED use 288 hours of [publicly available mocap data](https://huggingface.co/datasets/bones-studio/seed) so are less capable, but are useful for comparing to other models trained on BONES-SEED. To easily compare motion generation models to Kimodo, check out our [Motion Generation Benchmark](#kimodo-motion-generation-benchmark).
77
+
78
+ ### Changes in v1.1
79
+ The latest v1.1 Kimodo-SOMA models were released primarily for compatibility with our new [Motion Generation Benchmark](#kimodo-motion-generation-benchmark), but also contain minor quality improvements over v1. For details on these improvements, please see the Hugging Face pages for [Kimodo-SOMA-RP-v1.1](https://huggingface.co/nvidia/Kimodo-SOMA-RP-v1.1#changes-in-v11) and [Kimodo-SOMA-SEED-v1.1](https://huggingface.co/nvidia/Kimodo-SOMA-SEED-v1.1#changes-in-v11).
80
+
81
+ ## Getting Started
82
+
83
+ Please see the full documentation for detailed installation instructions, how to use the CLI and Interactive Demo, and other practical tips for generating motions with Kimodo:
84
+
85
+ **[Full Documentation](https://research.nvidia.com/labs/sil/projects/kimodo/docs)**
86
+ - [Quick Start Guide](https://research.nvidia.com/labs/sil/projects/kimodo/docs/getting_started/quick_start.html)
87
+ - [Installation Instructions](https://research.nvidia.com/labs/sil/projects/kimodo/docs/getting_started/installation.html)
88
+ - [Interactive Motion Authoring Demo](https://research.nvidia.com/labs/sil/projects/kimodo/docs/interactive_demo/index.html)
89
+ - [Command-Line Interface](https://research.nvidia.com/labs/sil/projects/kimodo/docs/user_guide/cli.html)
90
+ - [Benchmark Instructions](https://research.nvidia.com/labs/sil/projects/kimodo/docs/benchmark/introduction.html)
91
+ - [API Reference](https://research.nvidia.com/labs/sil/projects/kimodo/docs/api_reference/index.html)
92
+
93
+ **Before getting started** with motion generation, please review the [best practices](https://research.nvidia.com/labs/sil/projects/kimodo/docs/key_concepts/limitations.html) and be aware of [model limitations](https://research.nvidia.com/labs/sil/projects/kimodo/docs/key_concepts/limitations.html#limitations).
94
+
95
+
96
+ Some notes on installation environment:
97
+ - Kimodo requires ~17GB of VRAM to generate locally entirely on GPU, primarily due to the text embedding model. If you have a smaller card, set `TEXT_ENCODER_DEVICE=cpu` when running Kimodo commands to force text encoding to the CPU. This is slightly slower but reduces VRAM usage to <3 GB.
98
+ - The model has been most extensively tested on GeForce RTX 3090, GeForce RTX 4090, and NVIDIA A100 GPUs, but should work on other recent cards with sufficient VRAM
99
+ - This repo was developed on Linux, though Windows should work especially if using Docker
100
+
101
+ ## Interactive Motion Authoring Demo
102
+
103
+ <div align="center">
104
+ <img src="assets/demo_screenshot.png" width="1000">
105
+ </div>
106
+
107
+ </br>
108
+
109
+ **[Demo Documentation and Tutorial](https://research.nvidia.com/labs/sil/projects/kimodo/docs/interactive_demo/index.html)**
110
+
111
+ The web-based interactive demo provides an intuitive interface for generating motions with any of the Kimodo model variations. After installation, the demo can be launched with the `kimodo_demo` command. It runs locally on http://127.0.0.1:7860. Open this URL in your browser to access the interface (or use port forwarding if set up on a server).
112
+
113
+ ### Demo Features
114
+ - **Multiple Characters**: Supports generating with the SOMA, G1, and SMPL-X versions of Kimodo
115
+ - **Text Prompts**: Enter one or more natural language descriptions of desired motions on the timeline
116
+ - **Timeline Editor**: Add and edit keyframes and constrained intervals on multiple constraint tracks
117
+ - **Constraint Types**:
118
+ - Full-Body: Complete joint position constraints at specific frames
119
+ - 2D Root: Define waypoints or full paths to follow on the ground plane
120
+ - End-Effectors: Control hands and feet positions/rotations
121
+ - **Constraint Editing**: Editing mode allows for re-posing of constraints or adjusting waypoints
122
+ - **3D Visualization**: Real-time rendering of generated motions with skeleton and skinned mesh options
123
+ - **Playback Controls**: Preview generated motions with adjustable playback speed
124
+ - **Multiple Samples**: Generate and compare multiple motion variations
125
+ - **Examples**: Load pre-existing examples to better understand Kimodo's capabilities
126
+ - **Export**: Save constraints and generated motions for later use
127
+
128
+ ## Command-Line Interface
129
+
130
+ **[CLI Documentation and Examples](https://research.nvidia.com/labs/sil/projects/kimodo/docs/user_guide/cli.html)**
131
+
132
+ Motions can also be generated directly from the command line with the `kimodo_gen` command or by running `python -m kimodo.scripts.generate` directly.
133
+
134
+ **Key Arguments:**
135
+ - `prompt`: A single text description or sequence of texts for the desired motion (required)
136
+ - `--model`: Which Kimodo model to use for generation
137
+ - `--duration`: Motion duration in seconds
138
+ - `--num_samples`: Number of motion variations to generate
139
+ - `--constraints`: Constraint file to control the generated motion (e.g., saved from the web demo)
140
+ - `--diffusion_steps`: Number of denoising steps
141
+ - `--cfg_type` / `--cfg_weight`: Classifier-free guidance (`nocfg`, `regular` with one weight, or `separated` with two weights for text vs. constraints); see the [CLI docs](https://research.nvidia.com/labs/sil/projects/kimodo/docs/user_guide/cli.html#classifier-free-guidance-cfg)
142
+ - `--no-postprocess`: Flag to disable foot skate and constraint cleanup post-processing
143
+ - `--seed`: Random seed for reproducible results
144
+
145
+ The script supports different output formats depending on which skeleton is used. By default, a custom NPZ format is saved that is compatible with the web demo.
146
+ For Kimodo-G1 models, the motion can be saved in the standard MuJoCo qpos CSV format.
147
+ For Kimodo-SMPLX, motion can be saved in the standard AMASS npz format for compability with existing pipelines.
148
+
149
+ ### Default NPZ Output Format
150
+ Generated motions are saved as NPZ files containing:
151
+ - `posed_joints`: Global joint positions `[T, J, 3]`
152
+ - `global_rot_mats`: Global joint rotation matrices `[T, J, 3, 3]`
153
+ - `local_rot_mats`: Local (parent-relative) joint rotation matrices `[T, J, 3, 3]`
154
+ - `foot_contacts`: Foot contact labels [left heel, left toe, right heel, right toes] `[T, 4]`
155
+ - `smooth_root_pos`: Smoothed root representations outputted from the model `[T, 3]`
156
+ - `root_positions`: The (non-smoothed) trajectory of the actual root joint (e.g., pelvis) `[T, 3]`
157
+ - `global_root_heading`: The heading direction output from the model `[T, 2]`
158
+
159
+ `T` the number of frames and `J` the number of joints.
160
+
161
+ ## Low-Level Python API
162
+
163
+ **[Model API Documentation](https://research.nvidia.com/labs/sil/projects/kimodo/docs/api_reference/model.html#kimodo.model.kimodo_model.Kimodo.__call__)**
164
+
165
+ For maximum flexibility, the low-level model inference API can be called directly, rather than going through our high-level CLI.
166
+ This allows for advanced model configuration including classifier-free guidance weights and parameters related to transitions in multi-prompt sequences.
167
+
168
+ ## Downstream Robotics Applications of Kimodo
169
+
170
+ ### Visualizing G1 Motions with MuJoCo
171
+
172
+ <div align="center">
173
+ <img src="assets/mujoco_result.gif" width="800">
174
+ </div>
175
+
176
+ After generating motions on the G1 robot skeleton and saving to the MuJoCo qpos CSV file format, they can be easily used and visualized within MuJoCo.
177
+ A minimal visualization script is available with:
178
+ ```
179
+ python -m kimodo.scripts.mujoco_load
180
+ ```
181
+ Make sure to edit the script to correctly point to your CSV file and install Mujoco before running this.
182
+
183
+ ### Tracking Generated Motions with ProtoMotions
184
+
185
+ <div align="center">
186
+ <img src="assets/protomotions_results.gif" width="1280">
187
+ </div>
188
+
189
+ [ProtoMotions](https://github.com/NVlabs/ProtoMotions) is a GPU-accelerated simulation and learning framework for training physically simulated digital humans and humanoid robots. The Kimodo NPZ and CSV output formats are both compatible with ProtoMotions making it easy to train physics-based policies with generated motions from Kimodo. ProtoMotions supports outputs on both the SOMA skeleton and Unitree G1
190
+
191
+ After generating motions with Kimodo, head over to the [ProtoMotions docs](https://github.com/NVlabs/ProtoMotions?tab=readme-ov-file#-motion-authoring-with-kimodo) to see how to import them.
192
+
193
+ ### Retargeting Motions to Other Robots with GMR
194
+
195
+ <div align="center">
196
+ <img src="assets/gmr_results.gif" width="1280">
197
+ </div>
198
+
199
+ Motions generated by Kimodo-SMPLX can be retargeted to other robots using [General Motion Retargeting (GMR)](https://github.com/YanjieZe/GMR).
200
+ GMR supports the AMASS NPZ format out of the box, so simply generate motions with Kimodo and use `--output` to save; the AMASS NPZ is written to `stem_amass.npz` (single sample) or in the output folder (multiple samples). Then, use the [SMPL-X to Robot script](https://github.com/YanjieZe/GMR?tab=readme-ov-file#retargeting-from-smpl-x-amass-omomo-to-robot) in GMR to retarget to any supported robot. For example:
201
+ ```
202
+ # run within GMR codebase
203
+ python scripts/smplx_to_robot.py --smplx_file /path/to/saved/amass_format.npz --robot booster_t1
204
+ ```
205
+
206
+ ### Combining Kimodo with GEAR-SONIC
207
+
208
+ <div align="center">
209
+ <img src="assets/sonic_kimodo_demo.gif" width="800">
210
+ </div>
211
+
212
+ As a proof of concept, we have also incorporated Kimodo into the [interactive GEAR-SONIC demo](https://nvlabs.github.io/GEAR-SONIC/demo.html). In the demo, Kimodo can be used to generate a kinematic motion on the G1 robot skeleton, then GEAR-SONIC tracks the motion in simulation.
213
+
214
+ ## Kimodo Motion Generation Benchmark
215
+
216
+ [**[Benchmark Documentation](https://research.nvidia.com/labs/sil/projects/kimodo/docs/benchmark/introduction.html)**]
217
+ [**[Test Suite on Hugging Face](https://huggingface.co/datasets/nvidia/Kimodo-Motion-Gen-Benchmark)**]
218
+
219
+ Alongside the Kimodo models, we provide a benchmark designed to standardize evaluation for motion generation models with a comprehensive set of test cases. This includes:
220
+
221
+ * **Evaluation Data**: A suite of test cases [available on Hugging Face](https://huggingface.co/datasets/nvidia/Kimodo-Motion-Gen-Benchmark) is used in concert with the [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) dataset to construct the full benchmark.
222
+ * **Diverse Test Cases**: Test cases cover a wide range of text-conditioned and constraint-conditioned motion generation.
223
+ * **Evaluation Pipeline**: Code for the full evaluation pipeline including benchmark construction, motion generation, and evaluation.
224
+ * **Metrics**: Several metrics to evaluate generated motions that cover motion quality, constraint following, and text alignment. Our [TMR-SOMA-RP-v1](https://huggingface.co/nvidia/TMR-SOMA-RP-v1) model trained on all 700 hours of the Bones Rigplay dataset is a powerful embedding model to compute common metrics like R-precision and FID.
225
+
226
+ To facilitate future research, we [report benchmark results](https://research.nvidia.com/labs/sil/projects/kimodo/docs/benchmark/results.html) for Kimodo-SOMA-v1.1 models, which are reproducible and easily comparable to other methods trained on the BONES-SEED data.
227
+
228
+ ## Timeline Annotations for BONES-SEED
229
+
230
+ As detailed in the [tech report](https://research.nvidia.com/labs/sil/projects/kimodo/assets/kimodo_tech_report.pdf), Kimodo is trained using fine-grained temporal text annotations of mocap clips.
231
+ While the full [Rigplay 1](https://bones.studio/datasets#rp01) dataset is proprietary, we have released the temporal segmentations for the public [BONES-SEED](https://huggingface.co/datasets/bones-studio/seed) subset.
232
+ These annotations are already included in the BONES-SEED dataset, but the standalone labels and additional information about them is [available on HuggingFace](https://huggingface.co/datasets/nvidia/SEED-Timeline-Annotations).
233
+
234
+
235
+ ## Related Humanoid Work at NVIDIA
236
+ Kimodo is part of a larger effort to enable humanoid motion data for robotics, physical AI, and other applications.
237
+
238
+ Check out these related works:
239
+ * [ARDY](https://github.com/nv-tlabs/ardy) - builds on top of Kimodo to enable real-time controllable motion generation for interactive applications
240
+ * [MotionBricks](https://nvlabs.github.io/motionbricks/) - real-time motion generation framework that specializes in fast and robust motion in-betweening
241
+ * [SOMA Body Model](https://github.com/NVlabs/SOMA-X) - a unified parameteric human body model
242
+ * [BONES-SEED Dataset](https://huggingface.co/datasets/bones-studio/seed) - a large scale human(oid) motion capture dataset in SOMA and G1 format
243
+ * [ProtoMotions](https://github.com/NVlabs/ProtoMotions) - simulation and learning framework for training physically simulated human(oid)s
244
+ * [SOMA Retargeter](https://github.com/NVIDIA/soma-retargeter) - SOMA to G1 retargeting tool
245
+ * [GEM](https://github.com/NVlabs/GEM-X) - human motion reconstruction from video
246
+ * [GEAR SONIC](https://github.com/NVlabs/GR00T-WholeBodyControl) - humanoid behavior foundation model for physical robots
247
+
248
+ ## Citation
249
+
250
+ If you use this code in your research, please cite:
251
+
252
+ ```bibtex
253
+ @article{Kimodo2026,
254
+ title={Kimodo: Scaling Controllable Human Motion Generation},
255
+ author={Rempe, Davis and Petrovich, Mathis and Yuan, Ye and Zhang, Haotian and Peng, Xue Bin and Jiang, Yifeng and Wang, Tingwu and Iqbal, Umar and Minor, David and de Ruyter, Michael and Li, Jiefeng and Tessler, Chen and Lim, Edy and Jeong, Eugene and Wu, Sam and Hassani, Ehsan and Huang, Michael and Yu, Jin-Bey and Chung, Chaeyeon and Song, Lina and Dionne, Olivier and Kautz, Jan and Yuen, Simon and Fidler, Sanja},
256
+ journal={arXiv:2603.15546},
257
+ year={2026}
258
+ }
259
+ ```
260
+
261
+ ## License
262
+
263
+ This codebase is licensed under [Apache-2.0](LICENSE). Note that model checkpoints and data are licensed separately as indicated on the HuggingFace download pages.
264
+
265
+ This project will download and install additional third-party open source software projects. Review the license terms of these open source projects before use.
266
+
267
+ ## Acknowledgments
268
+
269
+ This project builds upon excellent open-source projects:
270
+ - [Viser](https://github.com/nerfstudio-project/viser) for 3D motion authoring demo
271
+ - [LLM2Vec](https://github.com/McGill-NLP/llm2vec) for text encoding
272
+
273
+ ## Contact
274
+
275
+ For questions or issues, please open an issue on this repository or reach out directly to the authors.
276
+
277
+ ---
278
+
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Minimal Kimodo text-to-motion API for Hugging Face ZeroGPU."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
10
+ os.environ.setdefault("TEXT_ENCODER_MODE", "local")
11
+ os.environ.setdefault("TEXT_ENCODER_DEVICE", "cuda")
12
+
13
+ # ZeroGPU must patch CUDA before torch or any Kimodo module imports it.
14
+ import spaces
15
+ import gradio as gr
16
+ import torch
17
+
18
+ from kimodo import load_model
19
+ from kimodo.exports.bvh import save_motion_bvh
20
+ from kimodo.exports.motion_io import save_kimodo_npz
21
+ from kimodo.tools import seed_everything
22
+ from space_utils import estimate_zero_gpu_duration, validate_motion_request
23
+
24
+
25
+ MODEL_NAME = os.environ.get("KIMODO_MODEL", "Kimodo-SOMA-RP-v1.1")
26
+
27
+ # Eager module-scope placement is required by ZeroGPU's weight packing mechanism.
28
+ MODEL, RESOLVED_MODEL_NAME = load_model(
29
+ MODEL_NAME,
30
+ device="cuda",
31
+ default_family="Kimodo",
32
+ return_resolved_name=True,
33
+ )
34
+
35
+
36
+ def _without_progress(iterable):
37
+ return iterable
38
+
39
+
40
+ def _single_sample(output: dict) -> dict:
41
+ """Remove the leading batch dimension from a one-sample model result."""
42
+
43
+ sample = {}
44
+ for key, value in output.items():
45
+ if hasattr(value, "shape") and len(value.shape) > 0 and int(value.shape[0]) == 1:
46
+ sample[key] = value[0]
47
+ else:
48
+ sample[key] = value
49
+ return sample
50
+
51
+
52
+ @spaces.GPU(duration=estimate_zero_gpu_duration, size="large")
53
+ def generate_motion(
54
+ prompt: str,
55
+ duration_seconds: float,
56
+ seed: int,
57
+ diffusion_steps: int,
58
+ standard_tpose: bool,
59
+ ) -> tuple[str, str, dict]:
60
+ """Generate one text-conditioned human motion and return BVH, NPZ, and metadata."""
61
+
62
+ request = validate_motion_request(
63
+ prompt,
64
+ duration_seconds,
65
+ seed,
66
+ diffusion_steps,
67
+ standard_tpose,
68
+ )
69
+ seed_everything(request.seed)
70
+
71
+ num_frames = max(1, int(round(request.duration_seconds * float(MODEL.fps))))
72
+ output = MODEL(
73
+ request.prompt,
74
+ num_frames,
75
+ num_denoising_steps=request.diffusion_steps,
76
+ multi_prompt=False,
77
+ constraint_lst=[],
78
+ cfg_weight=[2.0, 2.0],
79
+ num_samples=1,
80
+ return_numpy=True,
81
+ post_processing=False,
82
+ progress_bar=_without_progress,
83
+ )
84
+ sample = _single_sample(output)
85
+
86
+ output_dir = Path(tempfile.mkdtemp(prefix="kimodo-motion-"))
87
+ bvh_path = output_dir / "motion.bvh"
88
+ npz_path = output_dir / "motion.npz"
89
+ save_kimodo_npz(str(npz_path), sample)
90
+
91
+ skeleton = MODEL.output_skeleton
92
+ local_rot_mats = torch.as_tensor(sample["local_rot_mats"], device="cuda")
93
+ posed_joints = torch.as_tensor(sample["posed_joints"], device="cuda")
94
+ root_positions = posed_joints[:, int(skeleton.root_idx), :]
95
+ save_motion_bvh(
96
+ bvh_path,
97
+ local_rot_mats,
98
+ root_positions,
99
+ skeleton=skeleton,
100
+ fps=float(MODEL.fps),
101
+ standard_tpose=request.standard_tpose,
102
+ )
103
+
104
+ metadata = {
105
+ "prompt": request.prompt,
106
+ "duration_seconds": request.duration_seconds,
107
+ "seed": request.seed,
108
+ "diffusion_steps": request.diffusion_steps,
109
+ "model": MODEL_NAME,
110
+ "resolved_model": RESOLVED_MODEL_NAME,
111
+ "fps": float(MODEL.fps),
112
+ "frames": num_frames,
113
+ "skeleton": str(skeleton.name),
114
+ "standard_tpose": request.standard_tpose,
115
+ "post_processing": False,
116
+ }
117
+ return str(bvh_path), str(npz_path), metadata
118
+
119
+
120
+ with gr.Blocks(title="Kimodo Motion API") as demo:
121
+ gr.Markdown(
122
+ """
123
+ # Kimodo Motion API
124
+
125
+ Generate a text-conditioned SOMA motion on ZeroGPU. Download the BVH for Blender
126
+ or keep the NPZ for a later Kimodo workflow. The first call after an idle period may
127
+ take longer while ZeroGPU restores model weights.
128
+ """
129
+ )
130
+ prompt_input = gr.Textbox(
131
+ label="Motion prompt",
132
+ lines=3,
133
+ max_length=1_000,
134
+ value="A person walks forward cautiously, looks over the left shoulder, then stops.",
135
+ )
136
+ with gr.Row():
137
+ duration_input = gr.Slider(1.0, 10.0, value=5.0, step=0.5, label="Duration (seconds)")
138
+ seed_input = gr.Number(value=42, precision=0, minimum=0, maximum=2**31 - 1, label="Seed")
139
+ steps_input = gr.Slider(10, 100, value=50, step=5, label="Diffusion steps")
140
+ standard_tpose_input = gr.Checkbox(
141
+ value=True,
142
+ label="Export a standard T-pose rest skeleton",
143
+ info="Recommended for Blender retargeting.",
144
+ )
145
+ generate_button = gr.Button("Generate motion", variant="primary")
146
+ with gr.Row():
147
+ bvh_output = gr.File(label="Blender BVH")
148
+ npz_output = gr.File(label="Kimodo NPZ")
149
+ metadata_output = gr.JSON(label="Generation metadata")
150
+
151
+ generate_button.click(
152
+ fn=generate_motion,
153
+ inputs=[prompt_input, duration_input, seed_input, steps_input, standard_tpose_input],
154
+ outputs=[bvh_output, npz_output, metadata_output],
155
+ api_name="generate_motion",
156
+ api_description="Generate a Kimodo motion as BVH and NPZ files.",
157
+ concurrency_limit=1,
158
+ )
159
+ gr.Examples(
160
+ examples=[
161
+ ["A person takes three slow steps forward and waves with the right hand."],
162
+ ["A person crouches, jumps upward, lands, and regains balance."],
163
+ ["A person performs a short defensive boxing combination."],
164
+ ],
165
+ inputs=[prompt_input],
166
+ cache_examples=False,
167
+ )
168
+
169
+ demo.queue(default_concurrency_limit=1)
170
+
171
+ if __name__ == "__main__":
172
+ demo.launch(mcp_server=True)
assets/banner.png ADDED
assets/demo_screenshot.png ADDED

Git LFS Details

  • SHA256: f30c5ff61905841d7104ebecb9ca6321426cb383eaae32a1e6676adb1fc1442d
  • Pointer size: 131 Bytes
  • Size of remote file: 469 kB
assets/gmr_results.gif ADDED

Git LFS Details

  • SHA256: 76c317885ef2eee240c4859e1b98aca81ef5ae8cc8c0a140c1a0c44230b075cc
  • Pointer size: 132 Bytes
  • Size of remote file: 1.86 MB
assets/mujoco_result.gif ADDED

Git LFS Details

  • SHA256: 4988ec1722fa92b3154a305bfcc1c6b3d922e31f37d26f4ad11be4eaa481d07c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.85 MB