Spaces:
Runtime error
Runtime error
Deploy Kimodo ZeroGPU motion API
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +68 -0
- .gitignore +160 -0
- .pre-commit-config.yaml +50 -0
- ATTRIBUTIONS.MD +48 -0
- CHANGELOG.md +84 -0
- CONTRIBUTING.MD +49 -0
- Dockerfile +46 -0
- LICENSE +201 -0
- MANIFEST.in +6 -0
- MotionCorrection/.gitignore +91 -0
- MotionCorrection/CMakeLists.txt +95 -0
- MotionCorrection/MANIFEST.in +4 -0
- MotionCorrection/README.md +31 -0
- MotionCorrection/python/motion_correction/__init__.py +1 -0
- MotionCorrection/python/motion_correction/motion_postprocess.py +102 -0
- MotionCorrection/run_test.py +72 -0
- MotionCorrection/setup.py +122 -0
- MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.cpp +200 -0
- MotionCorrection/src/cpp/AnimProcessing/InverseKinematics.h +35 -0
- MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.cpp +524 -0
- MotionCorrection/src/cpp/AnimProcessing/TrajectoryCorrector.h +77 -0
- MotionCorrection/src/cpp/AnimProcessing/Utility.cpp +1197 -0
- MotionCorrection/src/cpp/AnimProcessing/Utility.h +54 -0
- MotionCorrection/src/cpp/BindingsPython.cpp +364 -0
- MotionCorrection/src/cpp/Compiler.h +25 -0
- MotionCorrection/src/cpp/Debug.h +13 -0
- MotionCorrection/src/cpp/Math/Constants.h +32 -0
- MotionCorrection/src/cpp/Math/Matrix.cpp +305 -0
- MotionCorrection/src/cpp/Math/Matrix.h +163 -0
- MotionCorrection/src/cpp/Math/Matrix.inl +802 -0
- MotionCorrection/src/cpp/Math/Quaternion.cpp +24 -0
- MotionCorrection/src/cpp/Math/Quaternion.h +144 -0
- MotionCorrection/src/cpp/Math/Quaternion.inl +606 -0
- MotionCorrection/src/cpp/Math/SIMD.h +100 -0
- MotionCorrection/src/cpp/Math/Scalar.h +187 -0
- MotionCorrection/src/cpp/Math/Transform.cpp +19 -0
- MotionCorrection/src/cpp/Math/Transform.h +213 -0
- MotionCorrection/src/cpp/Math/Transform.inl +508 -0
- MotionCorrection/src/cpp/Math/Types.cpp +44 -0
- MotionCorrection/src/cpp/Math/Types.h +808 -0
- MotionCorrection/src/cpp/Math/Vector.cpp +110 -0
- MotionCorrection/src/cpp/Math/Vector.h +454 -0
- MotionCorrection/src/cpp/Math/Vector.inl +2224 -0
- MotionCorrection/src/cpp/Platform.h +51 -0
- README.md +271 -6
- app.py +172 -0
- assets/banner.png +0 -0
- assets/demo_screenshot.png +3 -0
- assets/gmr_results.gif +3 -0
- 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
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.20.0
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
| 10 |
-
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
assets/gmr_results.gif
ADDED
|
Git LFS Details
|
assets/mujoco_result.gif
ADDED
|
Git LFS Details
|