diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0dd0e581b2..ce2cddd321 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,9 @@ A new header is inserted each time a *tag* is created. ## v1.12.1 (currently main branch) +- engine: `double` precision model (and view) matrix on `Camera`. This is only an + API change, internal precision is still `float` [⚠️ **API Change**]. + ## v1.12.0 - engine: Option to automatically compute bent normals from SSAO & apply to specular AO diff --git a/android/filament-android/src/main/cpp/Camera.cpp b/android/filament-android/src/main/cpp/Camera.cpp index a39c4ef9f9..800a4da303 100644 --- a/android/filament-android/src/main/cpp/Camera.cpp +++ b/android/filament-android/src/main/cpp/Camera.cpp @@ -102,10 +102,19 @@ Java_com_google_android_filament_Camera_nSetModelMatrix(JNIEnv *env, jclass, jlong nativeCamera, jfloatArray in_) { Camera* camera = (Camera *) nativeCamera; jfloat *in = env->GetFloatArrayElements(in_, NULL); - camera->setModelMatrix(*reinterpret_cast(in)); + camera->setModelMatrix((math::mat4)*reinterpret_cast(in)); env->ReleaseFloatArrayElements(in_, in, JNI_ABORT); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Camera_nSetModelMatrixFp64(JNIEnv *env, jclass, + jlong nativeCamera, jdoubleArray in_) { + Camera* camera = (Camera *) nativeCamera; + jdouble *in = env->GetDoubleArrayElements(in_, NULL); + camera->setModelMatrix(*reinterpret_cast(in)); + env->ReleaseDoubleArrayElements(in_, in, JNI_ABORT); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Camera_nGetProjectionMatrix(JNIEnv *env, jclass, jlong nativeCamera, jdoubleArray out_) { @@ -141,21 +150,41 @@ Java_com_google_android_filament_Camera_nGetModelMatrix(JNIEnv *env, jclass, jlong nativeCamera, jfloatArray out_) { Camera *camera = (Camera *) nativeCamera; jfloat *out = env->GetFloatArrayElements(out_, NULL); - const filament::math::mat4f& m = camera->getModelMatrix(); + const filament::math::mat4f& m = (math::mat4f)camera->getModelMatrix(); std::copy_n(&m[0][0], 16, out); env->ReleaseFloatArrayElements(out_, out, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Camera_nGetModelMatrixFp64(JNIEnv *env, jclass, + jlong nativeCamera, jdoubleArray out_) { + Camera *camera = (Camera *) nativeCamera; + jdouble *out = env->GetDoubleArrayElements(out_, NULL); + const filament::math::mat4& m = camera->getModelMatrix(); + std::copy_n(&m[0][0], 16, out); + env->ReleaseDoubleArrayElements(out_, out, 0); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Camera_nGetViewMatrix(JNIEnv *env, jclass, jlong nativeCamera, jfloatArray out_) { Camera *camera = (Camera *) nativeCamera; jfloat *out = env->GetFloatArrayElements(out_, NULL); - const filament::math::mat4f& m = camera->getViewMatrix(); + const filament::math::mat4f& m = (math::mat4f)camera->getViewMatrix(); std::copy_n(&m[0][0], 16, out); env->ReleaseFloatArrayElements(out_, out, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Camera_nGetViewMatrixFp64(JNIEnv *env, jclass, jlong nativeCamera, + jdoubleArray out_) { + Camera *camera = (Camera *) nativeCamera; + jdouble *out = env->GetDoubleArrayElements(out_, NULL); + const filament::math::mat4& m = camera->getViewMatrix(); + std::copy_n(&m[0][0], 16, out); + env->ReleaseDoubleArrayElements(out_, out, 0); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Camera_nGetPosition(JNIEnv *env, jclass, jlong nativeCamera, jfloatArray out_) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/Asserts.java b/android/filament-android/src/main/java/com/google/android/filament/Asserts.java index 759b1829f7..ac88bcff06 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Asserts.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Asserts.java @@ -63,12 +63,27 @@ final class Asserts { return out; } + @NonNull @Size(min = 16) + static double[] assertMat4(@Nullable double[] out) { + if (out == null) out = new double[16]; + else if (out.length < 16) { + throw new ArrayIndexOutOfBoundsException("Array length must be at least 16"); + } + return out; + } + static void assertMat4fIn(@NonNull @Size(min = 16) float[] in) { if (in.length < 16) { throw new ArrayIndexOutOfBoundsException("Array length must be at least 16"); } } + static void assertMat4In(@NonNull @Size(min = 16) double[] in) { + if (in.length < 16) { + throw new ArrayIndexOutOfBoundsException("Array length must be at least 16"); + } + } + @NonNull @Size(min = 3) static float[] assertFloat3(@Nullable float[] out) { if (out == null) out = new float[3]; diff --git a/android/filament-android/src/main/java/com/google/android/filament/Camera.java b/android/filament-android/src/main/java/com/google/android/filament/Camera.java index 9a9da5bbc2..ff3c35bfef 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Camera.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Camera.java @@ -419,6 +419,20 @@ public class Camera { nSetModelMatrix(getNativeObject(), viewMatrix); } + /** + * Sets the camera's view matrix. + *

+ * Helper method to set the camera's entity transform component. + * Remember that the Camera "looks" towards its -z axis. + *

+ * + * @param viewMatrix The camera position and orientation provided as a rigid transform matrix. + */ + public void setModelMatrix(@NonNull @Size(min = 16) double[] viewMatrix) { + Asserts.assertMat4In(viewMatrix); + nSetModelMatrixFp64(getNativeObject(), viewMatrix); + } + /** * Sets the camera's view matrix. * @@ -516,6 +530,22 @@ public class Camera { return out; } + /** + * Retrieves the camera's model matrix. The model matrix encodes the camera position and + * orientation, or pose. + * + * @param out A 16-double array where the model matrix will be stored, or null in which + * case a new array is allocated. + * + * @return A 16-double array containing the camera's pose as a column-major matrix. + */ + @NonNull @Size(min = 16) + public double[] getModelMatrix(@Nullable @Size(min = 16) double[] out) { + out = Asserts.assertMat4(out); + nGetModelMatrixFp64(getNativeObject(), out); + return out; + } + /** * Retrieves the camera's view matrix. The view matrix is the inverse of the model matrix. * @@ -531,6 +561,21 @@ public class Camera { return out; } + /** + * Retrieves the camera's view matrix. The view matrix is the inverse of the model matrix. + * + * @param out A 16-double array where the model view will be stored, or null in which + * case a new array is allocated. + * + * @return A 16-double array containing the camera's view as a column-major matrix. + */ + @NonNull @Size(min = 16) + public double[] getViewMatrix(@Nullable @Size(min = 16) double[] out) { + out = Asserts.assertMat4(out); + nGetViewMatrixFp64(getNativeObject(), out); + return out; + } + /** * Retrieves the camera position in world space. * @@ -740,6 +785,7 @@ public class Camera { private static native void nSetScaling(long nativeCamera, double x, double y); private static native void nSetShift(long nativeCamera, double x, double y); private static native void nSetModelMatrix(long nativeCamera, float[] in); + private static native void nSetModelMatrixFp64(long nativeCamera, double[] in); private static native void nLookAt(long nativeCamera, double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ); private static native float nGetNear(long nativeCamera); private static native float nGetCullingFar(long nativeCamera); @@ -747,7 +793,9 @@ public class Camera { private static native void nGetCullingProjectionMatrix(long nativeCamera, double[] out); private static native void nGetScaling(long nativeCamera, double[] out); private static native void nGetModelMatrix(long nativeCamera, float[] out); + private static native void nGetModelMatrixFp64(long nativeCamera, double[] out); private static native void nGetViewMatrix(long nativeCamera, float[] out); + private static native void nGetViewMatrixFp64(long nativeCamera, double[] out); private static native void nGetPosition(long nativeCamera, float[] out); private static native void nGetLeftVector(long nativeCamera, float[] out); private static native void nGetUpVector(long nativeCamera, float[] out); diff --git a/filament/include/filament/Camera.h b/filament/include/filament/Camera.h index 1b012807bd..f05740205e 100644 --- a/filament/include/filament/Camera.h +++ b/filament/include/filament/Camera.h @@ -329,7 +329,8 @@ public: * * @warning \p view must be a rigid transform */ - void setModelMatrix(const math::mat4f& view) noexcept; + void setModelMatrix(const math::mat4& view) noexcept; + void setModelMatrix(const math::mat4f& view) noexcept; //!< \overload /** Sets the camera's view matrix * @@ -362,10 +363,10 @@ public: * @return The camera's pose in world space as a rigid transform. Parent transforms, if any, * are taken into account. */ - math::mat4f getModelMatrix() const noexcept; + math::mat4 getModelMatrix() const noexcept; //! Returns the camera's view matrix (inverse of the model matrix) - math::mat4f getViewMatrix() const noexcept; + math::mat4 getViewMatrix() const noexcept; //! Returns the camera's position in world space math::float3 getPosition() const noexcept; diff --git a/filament/src/Camera.cpp b/filament/src/Camera.cpp index ff7603cb1b..9be1a79f99 100644 --- a/filament/src/Camera.cpp +++ b/filament/src/Camera.cpp @@ -196,16 +196,26 @@ void UTILS_NOINLINE FCamera::setModelMatrix(const mat4f& modelMatrix) noexcept { transformManager.setTransform(transformManager.getInstance(mEntity), modelMatrix); } +void UTILS_NOINLINE FCamera::setModelMatrix(const mat4& modelMatrix) noexcept { + FTransformManager& transformManager = mEngine.getTransformManager(); + // TODO: eventually setTransform() will accept mat4 directly + transformManager.setTransform(transformManager.getInstance(mEntity), mat4f(modelMatrix)); +} + void FCamera::lookAt(const float3& eye, const float3& center, const float3& up) noexcept { - setModelMatrix(mat4f::lookAt(eye, center, up)); + FTransformManager& transformManager = mEngine.getTransformManager(); + // TODO: eventually setTransform() will accept mat4 + transformManager.setTransform(transformManager.getInstance(mEntity), + mat4f::lookAt(eye, center, up)); } -mat4f const& FCamera::getModelMatrix() const noexcept { +mat4 FCamera::getModelMatrix() const noexcept { FTransformManager const& transformManager = mEngine.getTransformManager(); - return transformManager.getWorldTransform(transformManager.getInstance(mEntity)); + // TODO: eventually getWorldTransform() will return mat4 directly + return (mat4)transformManager.getWorldTransform(transformManager.getInstance(mEntity)); } -mat4f UTILS_NOINLINE FCamera::getViewMatrix() const noexcept { +mat4 UTILS_NOINLINE FCamera::getViewMatrix() const noexcept { return FCamera::getViewMatrix(getModelMatrix()); } @@ -275,13 +285,13 @@ math::details::TMat44 inverseProjection(const math::details::TMat44& p) no } UTILS_NOINLINE -mat4f FCamera::getViewMatrix(mat4f const& model) noexcept { +mat4 FCamera::getViewMatrix(mat4 const& model) noexcept { // We can't use rigidTransformInverse here. The camera's model matrix might have scaling, which // would make it non-rigid. return inverse(model); } -Frustum FCamera::getFrustum(mat4 const& projection, mat4f const& viewMatrix) noexcept { +Frustum FCamera::getFrustum(mat4 const& projection, mat4 const& viewMatrix) noexcept { return Frustum(mat4f{ projection * viewMatrix }); } @@ -290,8 +300,8 @@ Frustum FCamera::getFrustum(mat4 const& projection, mat4f const& viewMatrix) noe CameraInfo::CameraInfo(FCamera const& camera) noexcept { projection = mat4f{ camera.getProjectionMatrix() }; cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; - model = camera.getModelMatrix(); - view = camera.getViewMatrix(); + model = mat4f{ camera.getModelMatrix() }; + view = mat4f{camera.getViewMatrix() }; zn = camera.getNear(); zf = camera.getCullingFar(); ev100 = Exposure::ev100(camera); @@ -300,12 +310,12 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept { d = std::max(zn, camera.getFocusDistance()); } -CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept { - const mat4f modelMatrix{ worldOriginCamera * camera.getModelMatrix() }; +CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept { + const mat4 modelMatrix{ worldOriginCamera * camera.getModelMatrix() }; projection = mat4f{ camera.getProjectionMatrix() }; cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; - model = modelMatrix; - view = FCamera::getViewMatrix(model); + model = mat4f{ modelMatrix }; + view = mat4f{ FCamera::getViewMatrix(modelMatrix) }; zn = camera.getNear(); zf = camera.getCullingFar(); ev100 = Exposure::ev100(camera); @@ -313,7 +323,7 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCame A = f / camera.getAperture(); d = std::max(zn, camera.getFocusDistance()); worldOffset = camera.getPosition(); - worldOrigin = worldOriginCamera; + worldOrigin = mat4f{ worldOriginCamera }; } // ------------------------------------------------------------------------------------------------ @@ -383,6 +393,10 @@ float Camera::getCullingFar() const noexcept { return upcast(this)->getCullingFar(); } +void Camera::setModelMatrix(const mat4& modelMatrix) noexcept { + upcast(this)->setModelMatrix(modelMatrix); +} + void Camera::setModelMatrix(const mat4f& modelMatrix) noexcept { upcast(this)->setModelMatrix(modelMatrix); } @@ -395,11 +409,11 @@ void Camera::lookAt(const float3& eye, const float3& center) noexcept { upcast(this)->lookAt(eye, center, {0, 1, 0}); } -mat4f Camera::getModelMatrix() const noexcept { +mat4 Camera::getModelMatrix() const noexcept { return upcast(this)->getModelMatrix(); } -mat4f Camera::getViewMatrix() const noexcept { +mat4 Camera::getViewMatrix() const noexcept { return upcast(this)->getViewMatrix(); } diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index 6ea8144b67..aaeb2bfad0 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -47,7 +47,7 @@ FScene::FScene(FEngine& engine) : FScene::~FScene() noexcept = default; -void FScene::prepare(const mat4f& worldOriginTransform, bool shadowReceiversAreCasters) noexcept { +void FScene::prepare(const mat4& worldOriginTransform, bool shadowReceiversAreCasters) noexcept { // TODO: can we skip this in most cases? Since we rely on indices staying the same, // we could only skip, if nothing changed in the RCM. @@ -110,7 +110,9 @@ void FScene::prepare(const mat4f& worldOriginTransform, bool shadowReceiversAreC // get the world transform auto ti = tcm.getInstance(e); - const mat4f worldTransform = worldOriginTransform * tcm.getWorldTransform(ti); + // this is where we go from double to float for our transforms + // (in the future, getWorldTransform() will have a double version) + const mat4f worldTransform{ worldOriginTransform * tcm.getWorldTransform(ti) }; const bool reversedWindingOrder = det(worldTransform.upperLeft()) < 0; // don't even draw this object if it doesn't have a transform (which shouldn't happen diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index df67ce00e4..6e31de182f 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -453,7 +453,7 @@ void ShadowMap::computeShadowCameraDirectional( // The model matrix below is in fact inverted to get the view matrix and passed to the // shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric. // (see depth_main.fs). Note that in the case of VSM, 'b' below is identity. - mCamera->setModelMatrix(FCamera::rigidTransformInverse(Mv * b)); + mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) }); mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar); // for the debug camera, we need to undo the world origin @@ -508,7 +508,7 @@ void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::floa // The model matrix below is in fact inverted to get the view matrix and passed to the // shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric. // (see depth_main.fs). Note that in the case of VSM, 'b' below is identity. - mCamera->setModelMatrix(FCamera::rigidTransformInverse(Mv * b)); + mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) }); mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane); // for the debug camera, we need to undo the world origin diff --git a/filament/src/View.cpp b/filament/src/View.cpp index d77bcfd8e3..18d710f3d0 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -394,13 +394,13 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are * The "world origin" could also be useful for other things, like keeping the origin * close to the camera position to improve fp precision in the shader for large scenes. */ - mat4f worldOriginScene; + mat4 worldOriginScene; FIndirectLight const* const ibl = scene->getIndirectLight(); if (ibl) { // the IBL transformation must be a rigid transform mat3f rotation{ scene->getIndirectLight()->getRotation() }; // for a rigid-body transform, the inverse is the transpose - worldOriginScene = mat4f{ transpose(rotation) }; + worldOriginScene = mat4{ transpose(rotation) }; } /* diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index ab69ec3938..500f18b255 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -89,16 +89,17 @@ public: float getCullingFar() const noexcept { return mFar; } // sets the camera's view matrix (must be a rigid transform) + void setModelMatrix(const math::mat4& modelMatrix) noexcept; void setModelMatrix(const math::mat4f& modelMatrix) noexcept; // sets the camera's view matrix void lookAt(const math::float3& eye, const math::float3& center, const math::float3& up = { 0, 1, 0 }) noexcept; // returns the view matrix - math::mat4f const& getModelMatrix() const noexcept; + math::mat4 getModelMatrix() const noexcept; // returns the inverse of the view matrix - math::mat4f getViewMatrix() const noexcept; + math::mat4 getViewMatrix() const noexcept; template static math::details::TMat44 rigidTransformInverse(math::details::TMat44 const& v) noexcept { @@ -111,7 +112,7 @@ public: return math::details::TMat44(rt, -t); } - math::float3 const& getPosition() const noexcept { + math::double3 const& getPosition() const noexcept { return getModelMatrix()[3].xyz; } @@ -182,8 +183,8 @@ public: return mEntity; } - static math::mat4f getViewMatrix(math::mat4f const& model) noexcept; - static Frustum getFrustum(math::mat4 const& projection, math::mat4f const& viewMatrix) noexcept; + static math::mat4 getViewMatrix(math::mat4 const& model) noexcept; + static Frustum getFrustum(math::mat4 const& projection, math::mat4 const& viewMatrix) noexcept; private: FEngine& mEngine; @@ -206,7 +207,7 @@ private: struct CameraInfo { CameraInfo() noexcept = default; explicit CameraInfo(FCamera const& camera) noexcept; - CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept; + CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept; math::mat4f projection; // projection matrix for drawing (infinite zfar) math::mat4f cullingProjection; // projection matrix for culling diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 33895af75c..9dd9deca11 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -84,7 +84,7 @@ public: ~FScene() noexcept; void terminate(FEngine& engine); - void prepare(const math::mat4f& worldOriginTransform, bool shadowReceiversAreCasters) noexcept; + void prepare(const math::mat4& worldOriginTransform, bool shadowReceiversAreCasters) noexcept; void prepareDynamicLights(const CameraInfo& camera, ArenaScope& arena, backend::Handle lightUbh) noexcept; diff --git a/ios/samples/hello-gltf/hello-gltf/CameraManipulator.cpp b/ios/samples/hello-gltf/hello-gltf/CameraManipulator.cpp index 9386134c5b..208b8942f7 100644 --- a/ios/samples/hello-gltf/hello-gltf/CameraManipulator.cpp +++ b/ios/samples/hello-gltf/hello-gltf/CameraManipulator.cpp @@ -118,7 +118,7 @@ void CameraManipulator::updateCameraTransform() { mat4 rotate_y = mat4::rotation(mRotation.y, double3(0, 1, 0)); mat4 translate = mat4::translation(mTranslation); mat4 view = translate * (rotate_y * rotate_x * rotate_z); - mCamera->setModelMatrix(mat4f(view)); + mCamera->setModelMatrix(view); if (mCameraChanged) { mCameraChanged(mCamera); } diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index febd79b244..5f42abd097 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -782,11 +782,11 @@ class_("Camera") }), allow_raw_pointers()) .function("getModelMatrix", EMBIND_LAMBDA(flatmat4, (Camera* self), { - return flatmat4 { self->getModelMatrix() }; + return flatmat4 { (filament::math::mat4f)self->getModelMatrix() }; }), allow_raw_pointers()) .function("getViewMatrix", EMBIND_LAMBDA(flatmat4, (Camera* self), { - return flatmat4 { self->getViewMatrix() }; + return flatmat4 { (filament::math::mat4f)self->getViewMatrix() }; }), allow_raw_pointers()) .function("getPosition", &Camera::getPosition)