diff --git a/android/filament-android/src/main/cpp/Camera.cpp b/android/filament-android/src/main/cpp/Camera.cpp index 0d676b5b69..2e54e0b11d 100644 --- a/android/filament-android/src/main/cpp/Camera.cpp +++ b/android/filament-android/src/main/cpp/Camera.cpp @@ -54,6 +54,15 @@ Java_com_google_android_filament_Camera_nSetCustomProjection(JNIEnv *env, jclass env->ReleaseDoubleArrayElements(inMatrix_, inMatrix, JNI_ABORT); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Camera_nSetScaling(JNIEnv* env, jclass, + jlong nativeCamera, jdoubleArray inScaling_) { + Camera *camera = (Camera *) nativeCamera; + jdouble *inScaling = env->GetDoubleArrayElements(inScaling_, NULL); + camera->setScaling(*reinterpret_cast(inScaling)); + env->ReleaseDoubleArrayElements(inScaling_, inScaling, JNI_ABORT); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Camera_nLookAt(JNIEnv*, jclass, jlong nativeCamera, jdouble eye_x, jdouble eye_y, jdouble eye_z, jdouble center_x, jdouble center_y, @@ -94,6 +103,16 @@ Java_com_google_android_filament_Camera_nGetProjectionMatrix(JNIEnv *env, jclass env->ReleaseDoubleArrayElements(out_, out, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Camera_nGetScaling(JNIEnv *env, jclass, + jlong nativeCamera, jdoubleArray out_) { + Camera *camera = (Camera *) nativeCamera; + jdouble *out = env->GetDoubleArrayElements(out_, NULL); + const filament::math::double4& s = camera->getScaling(); + std::copy_n(&s[0], 4, out); + env->ReleaseDoubleArrayElements(out_, out, 0); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Camera_nGetModelMatrix(JNIEnv *env, jclass, jlong nativeCamera, jfloatArray out_) { diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index b3d3e546f6..4d6d0e51da 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -135,6 +135,14 @@ Java_com_google_android_filament_Engine_nCreateCameraWithEntity(JNIEnv*, jclass, return (jlong) engine->createCamera(entity); } +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_Engine_nGetCameraComponent(JNIEnv*, jclass, + jlong nativeEngine, jint entity_) { + Engine* engine = (Engine*) nativeEngine; + Entity& entity = *reinterpret_cast(&entity_); + return (jlong) engine->getCameraComponent(entity); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nDestroyCamera(JNIEnv*, jclass, jlong nativeEngine, jlong nativeCamera) { 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 ea718e5971..759b1829f7 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 @@ -98,4 +98,18 @@ final class Asserts { throw new ArrayIndexOutOfBoundsException("Array length must be at least 4"); } } + + static double[] assertDouble4(@Nullable double[] out) { + if (out == null) out = new double[4]; + else if (out.length < 4) { + throw new ArrayIndexOutOfBoundsException("Array length must be at least 4"); + } + return out; + } + + static void assertDouble4In(@NonNull double[] in) { + if (in.length < 4) { + throw new ArrayIndexOutOfBoundsException("Array length must be at least 4"); + } + } } 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 3deb723608..b1394e9a6d 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 @@ -276,6 +276,31 @@ public class Camera { nSetCustomProjection(getNativeObject(), inMatrix, near, far); } + /** + * Sets an additional matrix that scales the projection matrix. + * + *

This is useful to adjust the aspect ratio of the camera independent from its projection. + * First, pass an aspect of 1.0 to setProjection. Then set the scaling with the desired aspect + * ratio:
+ * + * + * camera->setScaling(double4 {1.0, width / height, 1.0, 1.0}); + * + * + * By default, this is an identity matrix. + *

+ * + * @param scaling diagonal of the scaling matrix to be applied after the projection matrix. + * + * @see Camera#setProjection + * @see Camera#setLensProjection + * @see Camera#setCustomProjection + */ + public void setScaling(@NonNull @Size(min = 4) double[] inScaling) { + Asserts.assertDouble4In(inScaling); + nSetScaling(getNativeObject(), inScaling); + } + /** * Sets the camera's view matrix. *

@@ -343,6 +368,20 @@ public class Camera { return out; } + /** + * Returns the scaling amount used to scale the projection matrix. + * + * @return the diagonal of the scaling matrix applied after the projection matrix. + * + * @see Camera#setScaling + */ + @NonNull @Size(min = 4) + public double[] getScaling(@Nullable @Size(min = 4) double[] out) { + out = Asserts.assertDouble4(out); + nGetScaling(getNativeObject(), out); + return out; + } + /** * Retrieves the camera's model matrix. The model matrix encodes the camera position and * orientation, or pose. @@ -521,11 +560,13 @@ public class Camera { private static native void nSetProjectionFov(long nativeCamera, double fovInDegrees, double aspect, double near, double far, int fov); private static native void nSetLensProjection(long nativeCamera, double focalLength, double aspect, double near, double far); private static native void nSetCustomProjection(long nativeCamera, double[] inMatrix, double near, double far); + private static native void nSetScaling(long nativeCamera, double[] inScaling); private static native void nSetModelMatrix(long nativeCamera, float[] 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); private static native void nGetProjectionMatrix(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 nGetViewMatrix(long nativeCamera, float[] out); private static native void nGetPosition(long nativeCamera, float[] out); diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 53b53a876a..ef2a146dba 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -17,6 +17,7 @@ package com.google.android.filament; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.filament.proguard.UsedByReflection; @@ -423,6 +424,20 @@ public class Engine { return new Camera(nativeCamera); } + /** + * Returns the Camera component of the given entity. + * + * @param entity An entity. + * @return the Camera component for this entity or null if the entity doesn't have a Camera + * component + */ + @Nullable + public Camera getCameraComponent(@Entity int entity) { + long nativeCamera = nGetCameraComponent(getNativeObject(), entity); + if (nativeCamera == 0) return null; + return new Camera(nativeCamera); + } + /** * Destroys a {@link Camera} component and frees all its associated resources. * @param camera the {@link Camera} to destroy @@ -662,6 +677,7 @@ public class Engine { private static native boolean nDestroyRenderer(long nativeEngine, long nativeRenderer); private static native long nCreateCamera(long nativeEngine); private static native long nCreateCameraWithEntity(long nativeEngine, int entity); + private static native long nGetCameraComponent(long nativeEngine, int entity); private static native void nDestroyCamera(long nativeEngine, long nativeCamera); private static native long nCreateScene(long nativeEngine); private static native boolean nDestroyScene(long nativeEngine, long nativeScene); diff --git a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp index 55f970cd0e..d4031df5c8 100644 --- a/android/gltfio-android/src/main/cpp/FilamentAsset.cpp +++ b/android/gltfio-android/src/main/cpp/FilamentAsset.cpp @@ -127,6 +127,24 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetLightEntities(JNIEnv* env->ReleaseIntArrayElements(result, (jint*) entities, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_gltfio_FilamentAsset_nGetCameraEntities(JNIEnv* env, jclass, + jlong nativeAsset, jintArray result) { + FilamentAsset* asset = (FilamentAsset*) nativeAsset; + jsize available = env->GetArrayLength(result); + Entity* entities = (Entity*) env->GetIntArrayElements(result, nullptr); + std::copy_n(asset->getCameraEntities(), + std::min(available, (jsize) asset->getCameraEntityCount()), entities); + env->ReleaseIntArrayElements(result, (jint*) entities, 0); +} + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_gltfio_FilamentAsset_nGetCameraEntityCount(JNIEnv*, jclass, + jlong nativeAsset) { + FilamentAsset* asset = (FilamentAsset*) nativeAsset; + return asset->getCameraEntityCount(); +} + extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstanceCount(JNIEnv*, jclass, jlong nativeAsset) { diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java index f16c3cf1e7..55f391b7cf 100644 --- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java +++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/FilamentAsset.java @@ -112,6 +112,28 @@ public class FilamentAsset { return result; } + /** + * Gets only the entities that have camera components. + * + *

+ * Note about aspect ratios:
+ * + * gltfio always uses an aspect ratio of 1.0 when setting the projection matrix for perspective + * cameras. gltfio then sets the camera's scaling matrix with the aspect ratio specified in the + * glTF file (if present).
+ * + * The camera's scaling matrix allows clients to adjust the aspect ratio independently from the + * camera's projection. + *

+ * + * @see com.google.android.filament.Camera#setScaling + */ + public @NonNull @Entity int[] getCameraEntities() { + int[] result = new int[nGetCameraEntityCount(mNativeObject)]; + nGetCameraEntities(mNativeObject, result); + return result; + } + /** * Returns the first entity with the given name, or 0 if none exist. */ @@ -217,6 +239,9 @@ public class FilamentAsset { private static native int nGetLightEntityCount(long nativeAsset); private static native void nGetLightEntities(long nativeAsset, int[] result); + private static native int nGetCameraEntityCount(long nativeAsset); + private static native void nGetCameraEntities(long nativeAsset, int[] result); + private static native int nGetMaterialInstanceCount(long nativeAsset); private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index e93bceafd3..7e52332cc6 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -307,7 +307,7 @@ public: Camera* createCamera(utils::Entity entity) noexcept; /** - * Returns the Camera component of the given its entity. + * Returns the Camera component of the given entity. * * @param entity An entity. * @return A pointer to the Camera component for this entity or nullptr if the entity didn't