diff --git a/android/filament-android/src/main/cpp/TransformManager.cpp b/android/filament-android/src/main/cpp/TransformManager.cpp index c4b2d3895d..484c711e00 100644 --- a/android/filament-android/src/main/cpp/TransformManager.cpp +++ b/android/filament-android/src/main/cpp/TransformManager.cpp @@ -70,6 +70,23 @@ Java_com_google_android_filament_TransformManager_nCreateArray(JNIEnv* env, return tm->getInstance(entity); } +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_TransformManager_nCreateArrayFp64(JNIEnv* env, + jclass, jlong nativeTransformManager, jint entity_, jint parent, + jdoubleArray localTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); + if (localTransform_) { + jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL); + tm->create(entity, (TransformManager::Instance) parent, + *reinterpret_cast(localTransform)); + env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT); + } else { + tm->create(entity, (TransformManager::Instance) parent); + } + return tm->getInstance(entity); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_TransformManager_nDestroy(JNIEnv*, jclass, jlong nativeTransformManager, jint entity_) { @@ -104,6 +121,17 @@ Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv* env, env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_TransformManager_nSetTransformFp64(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jdoubleArray localTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + jdouble *localTransform = env->GetDoubleArrayElements(localTransform_, NULL); + tm->setTransform((TransformManager::Instance) i, + *reinterpret_cast(localTransform)); + env->ReleaseDoubleArrayElements(localTransform_, localTransform, JNI_ABORT); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_TransformManager_nGetTransform(JNIEnv* env, jclass, jlong nativeTransformManager, jint i, @@ -115,6 +143,17 @@ Java_com_google_android_filament_TransformManager_nGetTransform(JNIEnv* env, env->ReleaseFloatArrayElements(outLocalTransform_, outLocalTransform, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_TransformManager_nGetTransformFp64(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jdoubleArray outLocalTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + jdouble *outLocalTransform = env->GetDoubleArrayElements(outLocalTransform_, NULL); + *reinterpret_cast(outLocalTransform) = tm->getTransformAccurate( + (TransformManager::Instance) i); + env->ReleaseDoubleArrayElements(outLocalTransform_, outLocalTransform, 0); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_TransformManager_nGetWorldTransform(JNIEnv* env, jclass, jlong nativeTransformManager, jint i, @@ -126,6 +165,17 @@ Java_com_google_android_filament_TransformManager_nGetWorldTransform(JNIEnv* env env->ReleaseFloatArrayElements(outWorldTransform_, outWorldTransform, 0); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_TransformManager_nGetWorldTransformFp64(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jdoubleArray outWorldTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + jdouble *outWorldTransform = env->GetDoubleArrayElements(outWorldTransform_, NULL); + *reinterpret_cast(outWorldTransform) = tm->getWorldTransformAccurate( + (TransformManager::Instance) i); + env->ReleaseDoubleArrayElements(outWorldTransform_, outWorldTransform, 0); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_TransformManager_nOpenLocalTransformTransaction( JNIEnv*, jclass, jlong nativeTransformManager) { @@ -139,3 +189,19 @@ Java_com_google_android_filament_TransformManager_nCommitLocalTransformTransacti TransformManager* tm = (TransformManager*) nativeTransformManager; tm->commitLocalTransformTransaction(); } + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_TransformManager_nSetAccurateTranslationsEnabled(JNIEnv*, + jclass, jlong nativeTransformManager, jboolean enable) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + tm->setAccurateTranslationsEnabled((bool)enable); +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_TransformManager_nIsAccurateTranslationsEnabled(JNIEnv*, + jclass, jlong nativeTransformManager) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + return (jboolean)tm->isAccurateTranslationsEnabled(); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java index e67f882e80..7f319b56a3 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java @@ -83,6 +83,35 @@ import androidx.annotation.Size; return nGetInstance(mNativeObject, entity); } + /** + * Enables or disable the accurate translation mode. Disabled by default. + * + * When accurate translation mode is active, the translation component of all transforms is + * maintained at double precision. This is only useful if the mat4 version of setTransform() + * is used, as well as getTransformAccurate(). + * + * @param enable true to enable the accurate translation mode, false to disable. + * + * @see #isAccurateTranslationsEnabled + * @see #create(int, int, double[]) + * @see #setTransform(int, double[]) + * @see #getTransform(int, double[]) + * @see #getWorldTransform(int, double[]) + */ + public void setAccurateTranslationsEnabled(boolean enable) { + nSetAccurateTranslationsEnabled(mNativeObject, enable); + } + + /** + * Returns whether the high precision translation mode is active. + * + * @return true if accurate translations mode is active, false otherwise + * @see #setAccurateTranslationsEnabled + */ + public boolean isAccurateTranslationsEnabled() { + return nIsAccurateTranslationsEnabled(mNativeObject); + } + /** * Creates a transform component and associates it with the given entity. The component is * initialized with the identity transform. @@ -114,6 +143,23 @@ import androidx.annotation.Size; return nCreateArray(mNativeObject, entity, parent, localTransform); } + /** + * Creates a transform component with a parent and associates it with the given entity. + * If this component already exists on the given entity, it is first + * destroyed as if {@link #destroy} was called. + * + * @param entity an {@link Entity} to associate a transform component to. + * @param parent the {@link EntityInstance} of the parent transform + * @param localTransform the transform, relative to the parent, to initialize the transform + * component with. + * @see #destroy + */ + @EntityInstance + public int create(@Entity int entity, @EntityInstance int parent, + @Nullable @Size(min = 16) double[] localTransform) { + return nCreateArrayFp64(mNativeObject, entity, parent, localTransform); + } + /** * Destroys this component from the given entity, children are orphaned. * @@ -170,6 +216,24 @@ import androidx.annotation.Size; nSetTransform(mNativeObject, i, localTransform); } + /** + * Sets a local transform of a transform component. + *

This operation can be slow if the hierarchy of transform is too deep, and this + * will be particularly bad when updating a lot of transforms. In that case, + * consider using {@link #openLocalTransformTransaction} / {@link #commitLocalTransformTransaction}.

+ * + * @param i the {@link EntityInstance} of the transform component to set the local + * transform to. + * @param localTransform the local transform (i.e. relative to the parent). + * @see #getTransform(int, double[]) + * @see #getWorldTransform(int, double[]) + */ + public void setTransform(@EntityInstance int i, + @NonNull @Size(min = 16) double[] localTransform) { + Asserts.assertMat4In(localTransform); + nSetTransformFp64(mNativeObject, i, localTransform); + } + /** * Returns the local transform of a transform component. * @@ -190,6 +254,26 @@ import androidx.annotation.Size; return outLocalTransform; } + /** + * Returns the local transform of a transform component. + * + * @param i the {@link EntityInstance} of the transform component to query the + * local transform from. + * @param outLocalTransform a 16 float array to receive the result. + * If null is given, a new suitable array is allocated. + * @return the local transform of the component (i.e. relative to the parent). This always + * returns the value set by setTransform(). + * @see #setTransform + */ + @NonNull + @Size(min = 16) + public double[] getTransform(@EntityInstance int i, + @Nullable @Size(min = 16) double[] outLocalTransform) { + outLocalTransform = Asserts.assertMat4(outLocalTransform); + nGetTransformFp64(mNativeObject, i, outLocalTransform); + return outLocalTransform; + } + /** * Returns the world transform of a transform component. * @@ -210,6 +294,26 @@ import androidx.annotation.Size; return outWorldTransform; } + /** + * Returns the world transform of a transform component. + * + * @param i the {@link EntityInstance} of the transform component to query the + * world transform from. + * @param outWorldTransform a 16 float array to receive the result. + * If null is given, a new suitable array is allocated + * @return The world transform of the component (i.e. relative to the root). This is the + * composition of this component's local transform with its parent's world transform. + * @see #setTransform + */ + @NonNull + @Size(min = 16) + public double[] getWorldTransform(@EntityInstance int i, + @Nullable @Size(min = 16) double[] outWorldTransform) { + outWorldTransform = Asserts.assertMat4(outWorldTransform); + nGetWorldTransformFp64(mNativeObject, i, outWorldTransform); + return outWorldTransform; + } + /** * Opens a local transform transaction. During a transaction, {@link #getWorldTransform} can * return an invalid transform until {@link #commitLocalTransformTransaction} is called. @@ -252,12 +356,18 @@ import androidx.annotation.Size; private static native int nGetInstance(long nativeTransformManager, int entity); private static native int nCreate(long nativeTransformManager, int entity); private static native int nCreateArray(long mNativeObject, int entity, int parent, float[] localTransform); + private static native int nCreateArrayFp64(long mNativeObject, int entity, int parent, double[] localTransform); private static native void nDestroy(long nativeTransformManager, int entity); private static native void nSetParent(long nativeTransformManager, int i, int newParent); private static native int nGetParent(long nativeTransformManager, int i); private static native void nSetTransform(long nativeTransformManager, int i, float[] localTransform); + private static native void nSetTransformFp64(long nativeTransformManager, int i, double[] localTransform); private static native void nGetTransform(long nativeTransformManager, int i, float[] outLocalTransform); + private static native void nGetTransformFp64(long nativeTransformManager, int i, double[] outLocalTransform); private static native void nGetWorldTransform(long nativeTransformManager, int i, float[] outWorldTransform); + private static native void nGetWorldTransformFp64(long nativeTransformManager, int i, double[] outWorldTransform); private static native void nOpenLocalTransformTransaction(long nativeTransformManager); private static native void nCommitLocalTransformTransaction(long nativeTransformManager); + private static native void nSetAccurateTranslationsEnabled(long nativeTransformManager, boolean enable); + private static native boolean nIsAccurateTranslationsEnabled(long nativeTransformManager); } diff --git a/filament/include/filament/TransformManager.h b/filament/include/filament/TransformManager.h index 8d9c7d8dde..f690e766f9 100644 --- a/filament/include/filament/TransformManager.h +++ b/filament/include/filament/TransformManager.h @@ -112,6 +112,30 @@ public: */ Instance getInstance(utils::Entity e) const noexcept; + /** + * Enables or disable the accurate translation mode. Disabled by default. + * + * When accurate translation mode is active, the translation component of all transforms is + * maintained at double precision. This is only useful if the mat4 version of setTransform() + * is used, as well as getTransformAccurate(). + * + * @param enable true to enable the accurate translation mode, false to disable. + * + * @see isAccurateTranslationsEnabled + * @see create(utils::Entity, Instance, const math::mat4&); + * @see setTransform(Instance, const math::mat4&) + * @see getTransformAccurate + * @see getWorldTransformAccurate + */ + void setAccurateTranslationsEnabled(bool enable) noexcept; + + /** + * Returns whether the high precision translation mode is active. + * @return true if accurate translations mode is active, false otherwise + * @see setAccurateTranslationsEnabled + */ + bool isAccurateTranslationsEnabled() const noexcept; + /** * Creates a transform component and associate it with the given entity. * @param entity An Entity to associate a transform component to. @@ -125,7 +149,8 @@ public: * @see destroy() */ void create(utils::Entity entity, Instance parent, const math::mat4f& localTransform); - void create(utils::Entity entity, Instance parent = {}); + void create(utils::Entity entity, Instance parent, const math::mat4& localTransform); //!< \overload + void create(utils::Entity entity, Instance parent = {}); //!< \overload /** * Destroys this component from the given entity, children are orphaned. @@ -202,6 +227,18 @@ public: */ void setTransform(Instance ci, const math::mat4f& localTransform) noexcept; + /** + * Sets a local transform of a transform component and keeps double precision translation. + * All other values of the transform are stored at single precision. + * @param ci The instance of the transform component to set the local transform to. + * @param localTransform The local transform (i.e. relative to the parent). + * @see getTransform() + * @attention This operation can be slow if the hierarchy of transform is too deep, and this + * will be particularly bad when updating a lot of transforms. In that case, + * consider using openLocalTransformTransaction() / commitLocalTransformTransaction(). + */ + void setTransform(Instance ci, const math::mat4& localTransform) noexcept; + /** * Returns the local transform of a transform component. * @param ci The instance of the transform component to query the local transform from. @@ -211,6 +248,15 @@ public: */ const math::mat4f& getTransform(Instance ci) const noexcept; + /** + * Returns the local transform of a transform component. + * @param ci The instance of the transform component to query the local transform from. + * @return The local transform of the component (i.e. relative to the parent). This always + * returns the value set by setTransform(). + * @see setTransform() + */ + const math::mat4 getTransformAccurate(Instance ci) const noexcept; + /** * Return the world transform of a transform component. * @param ci The instance of the transform component to query the world transform from. @@ -220,6 +266,15 @@ public: */ const math::mat4f& getWorldTransform(Instance ci) const noexcept; + /** + * Return the world transform of a transform component. + * @param ci The instance of the transform component to query the world transform from. + * @return The world transform of the component (i.e. relative to the root). This is the + * composition of this component's local transform with its parent's world transform. + * @see setTransform() + */ + const math::mat4 getWorldTransformAccurate(Instance ci) const noexcept; + /** * Opens a local transform transaction. During a transaction, getWorldTransform() can * return an invalid transform until commitLocalTransformTransaction() is called. However, diff --git a/filament/src/components/TransformManager.cpp b/filament/src/components/TransformManager.cpp index 79277f329c..74918f333c 100644 --- a/filament/src/components/TransformManager.cpp +++ b/filament/src/components/TransformManager.cpp @@ -19,6 +19,8 @@ #include #include +#include + using namespace utils; using namespace filament::math; @@ -32,8 +34,18 @@ FTransformManager::~FTransformManager() noexcept = default; void FTransformManager::terminate() noexcept { } +void FTransformManager::setAccurateTranslationsEnabled(bool enable) noexcept { + if (enable != mAccurateTranslations) { + mAccurateTranslations = enable; + // when enabling accurate translations, we have to recompute all world transforms + if (enable && !mLocalTransformTransactionOpen) { + computeAllWorldTransforms(); + } + } +} + void FTransformManager::create(Entity entity) { - create(entity, 0, {}); + create(entity, 0, mat4f{}); } void FTransformManager::create(Entity entity, Instance parent, const mat4f& localTransform) { @@ -58,6 +70,28 @@ void FTransformManager::create(Entity entity, Instance parent, const mat4f& loca } } +void FTransformManager::create(Entity entity, Instance parent, const mat4& localTransform) { + // this always adds at the end, so all existing instances stay valid + auto& manager = mManager; + + // TODO: try to keep entries sorted with their siblings/parents to improve cache access + if (UTILS_UNLIKELY(manager.hasComponent(entity))) { + destroy(entity); + } + Instance i = manager.addComponent(entity); + assert_invariant(i); + assert_invariant(i != parent); + + if (i && i != parent) { + manager[i].parent = 0; + manager[i].next = 0; + manager[i].prev = 0; + manager[i].firstChild = 0; + insertNode(i, parent); + setTransform(i, localTransform); + } +} + void FTransformManager::setParent(Instance i, Instance parent) noexcept { validateNode(i); if (i) { @@ -139,6 +173,18 @@ void FTransformManager::setTransform(Instance ci, const mat4f& model) noexcept { auto& manager = mManager; // store our local transform manager[ci].local = model; + manager[ci].localTranslationLo = {}; + updateNodeTransform(ci); + } +} + +void FTransformManager::setTransform(Instance ci, const mat4& model) noexcept { + validateNode(ci); + if (ci) { + auto& manager = mManager; + // store our local transform + accurate translation information + manager[ci].local = mat4f(model); + manager[ci].localTranslationLo = float3{ model[3].xyz - float3{ model[3].xyz }}; updateNodeTransform(ci); } } @@ -155,10 +201,10 @@ void FTransformManager::updateNodeTransform(Instance i) noexcept { // find our parent's world transform, if any // note: by using the raw_array() we don't need to check that parent is valid. Instance parent = manager[i].parent; - mat4f const& pt = manager.raw_array()[parent]; - - // compute our world transform - manager[i].world = pt * static_cast(manager[i].local); + computeWorldTransform(manager[i].world, manager[i].worldTranslationLo, + manager[parent].world, manager[i].local, + manager[parent].worldTranslationLo, manager[i].localTranslationLo, + mAccurateTranslations); // update our children's world transforms Instance child = manager[i].firstChild; @@ -174,22 +220,30 @@ void FTransformManager::openLocalTransformTransaction() noexcept { void FTransformManager::commitLocalTransformTransaction() noexcept { if (mLocalTransformTransactionOpen) { mLocalTransformTransactionOpen = false; - auto& manager = mManager; + computeAllWorldTransforms(); + } +} - // swapNode() below needs some temporary storage which we provide here - auto& soa = manager.getSoA(); - soa.ensureCapacity(soa.size() + 1); +void FTransformManager::computeAllWorldTransforms() noexcept { + auto& manager = mManager; - mat4f const* const UTILS_RESTRICT world = manager.raw_array(); - for (Instance i = manager.begin(), e = manager.end(); i != e; ++i) { - // Ensure that children are always sorted after their parent. - while (UTILS_UNLIKELY(Instance(manager[i].parent) > i)) { - swapNode(i, manager[i].parent); - } - Instance parent = manager[i].parent; - assert_invariant(parent < i); - manager[i].world = world[parent] * static_cast(manager[i].local); + // swapNode() below needs some temporary storage which we provide here + const bool accurate = mAccurateTranslations; + auto& soa = manager.getSoA(); + soa.ensureCapacity(soa.size() + 1); + + for (Instance i = manager.begin(), e = manager.end(); i != e; ++i) { + // Ensure that children are always sorted after their parent. + while (UTILS_UNLIKELY(Instance(manager[i].parent) > i)) { + swapNode(i, manager[i].parent); } + Instance parent = manager[i].parent; + assert_invariant(parent < i); + + computeWorldTransform(manager[i].world, manager[i].worldTranslationLo, + manager[parent].world, manager[i].local, + manager[parent].worldTranslationLo, manager[i].localTranslationLo, + accurate); } } @@ -313,25 +367,62 @@ void FTransformManager::updateNode(Instance i) noexcept { validateNode(next); } -void FTransformManager::transformChildren(Sim& manager, Instance ci) noexcept { - while (ci) { +void FTransformManager::transformChildren(Sim& manager, Instance i) noexcept { + const bool accurate = mAccurateTranslations; + while (i) { // update child's world transform - Instance parent = manager[ci].parent; - mat4f const& pt = manager[parent].world; - mat4f const& local = manager[ci].local; - manager[ci].world = pt * local; + Instance parent = manager[i].parent; + computeWorldTransform(manager[i].world, manager[i].worldTranslationLo, + manager[parent].world, manager[i].local, + manager[parent].worldTranslationLo, manager[i].localTranslationLo, + accurate); // assume we don't have a deep hierarchy - Instance child = manager[ci].firstChild; + Instance child = manager[i].firstChild; if (UTILS_UNLIKELY(child)) { transformChildren(manager, child); } // process our next child - ci = manager[ci].next; + i = manager[i].next; } } +void FTransformManager::computeWorldTransform( + mat4f& UTILS_RESTRICT outWorld, + float3& UTILS_RESTRICT inoutWorldTranslationLo, + mat4f const& UTILS_RESTRICT pt, + mat4f const& UTILS_RESTRICT local, + float3 const& UTILS_RESTRICT ptTranslationLo, // reference to avoid unneeded access + float3 const& UTILS_RESTRICT localTranslationLo, // reference to avoid unneeded access + bool accurate) { + + outWorld[0] = pt * local[0]; + outWorld[1] = pt * local[1]; + outWorld[2] = pt * local[2]; + + // "a branch not taken is free", i.e.: we burn a BT cache entry only in the accurate case + if (UTILS_LIKELY(!accurate)) { + outWorld[3] = pt * local[3]; + } else { + // this version takes the extra precision of the translation into account, + // we assume that the last row of local is [0 0 0 x]. + // Only the last column of the result needs special treatment -- unfortunately this requires + // converting 'pt' to a mat4 (double) + + const mat4 ptd{ + pt[0], pt[1], pt[2], + double4{ pt[3].xyz + ptTranslationLo, pt[3].w }}; + + const double4 worldTranslation = + ptd * double4{ local[3].xyz + localTranslationLo, local[3].w }; + + inoutWorldTranslationLo = worldTranslation.xyz - float3{ worldTranslation.xyz }; + outWorld[3] = worldTranslation; + } +} + + void FTransformManager::validateNode(Instance i) noexcept { #ifndef NDEBUG auto& manager = mManager; @@ -395,8 +486,12 @@ void TransformManager::create(Entity entity, Instance parent, const mat4f& world upcast(this)->create(entity, parent, worldTransform); } +void TransformManager::create(Entity entity, Instance parent, const mat4& worldTransform) { + upcast(this)->create(entity, parent, worldTransform); +} + void TransformManager::create(Entity entity, Instance parent) { - upcast(this)->create(entity, parent, {}); + upcast(this)->create(entity, parent, mat4f{}); } void TransformManager::destroy(Entity e) noexcept { @@ -415,14 +510,26 @@ void TransformManager::setTransform(Instance ci, const mat4f& model) noexcept { upcast(this)->setTransform(ci, model); } +void TransformManager::setTransform(Instance ci, const mat4& model) noexcept { + upcast(this)->setTransform(ci, model); +} + const mat4f& TransformManager::getTransform(Instance ci) const noexcept { return upcast(this)->getTransform(ci); } +const mat4 TransformManager::getTransformAccurate(Instance ci) const noexcept { + return upcast(this)->getTransformAccurate(ci); +} + const mat4f& TransformManager::getWorldTransform(Instance ci) const noexcept { return upcast(this)->getWorldTransform(ci); } +const mat4 TransformManager::getWorldTransformAccurate(Instance ci) const noexcept { + return upcast(this)->getWorldTransformAccurate(ci); +} + void TransformManager::setParent(Instance i, Instance newParent) noexcept { upcast(this)->setParent(i, newParent); } @@ -458,4 +565,12 @@ TransformManager::children_iterator TransformManager::getChildrenEnd( return upcast(this)->getChildrenEnd(parent); } +void TransformManager::setAccurateTranslationsEnabled(bool enable) noexcept { + upcast(this)->setAccurateTranslationsEnabled(enable); +} + +bool TransformManager::isAccurateTranslationsEnabled() const noexcept { + return upcast(this)->isAccurateTranslationsEnabled();; +} + } // namespace filament diff --git a/filament/src/components/TransformManager.h b/filament/src/components/TransformManager.h index c742932dc4..37524be205 100644 --- a/filament/src/components/TransformManager.h +++ b/filament/src/components/TransformManager.h @@ -53,10 +53,18 @@ public: return Instance(mManager.getInstance(e)); } + void setAccurateTranslationsEnabled(bool enable) noexcept; + + bool isAccurateTranslationsEnabled() const noexcept { + return mAccurateTranslations; + } + void create(utils::Entity entity); void create(utils::Entity entity, Instance parent, const math::mat4f& localTransform); + void create(utils::Entity entity, Instance parent, const math::mat4& localTransform); + void destroy(utils::Entity e) noexcept; void setParent(Instance i, Instance newParent) noexcept; @@ -83,6 +91,8 @@ public: void setTransform(Instance ci, const math::mat4f& model) noexcept; + void setTransform(Instance ci, const math::mat4& model) noexcept; + const math::mat4f& getTransform(Instance ci) const noexcept { return mManager[ci].local; } @@ -91,6 +101,22 @@ public: return mManager[ci].world; } + math::mat4 getTransformAccurate(Instance ci) const noexcept { + math::mat4f const& local = mManager[ci].local; + math::float3 localTranslationLo = mManager[ci].localTranslationLo; + math::mat4 r(local); + r[3].xyz += localTranslationLo; + return r; + } + + math::mat4 getWorldTransformAccurate(Instance ci) const noexcept { + math::mat4f const& world = mManager[ci].world; + math::float3 worldTranslationLo = mManager[ci].worldTranslationLo; + math::mat4 r(world); + r[3].xyz += worldTranslationLo; + return r; + } + private: struct Sim; @@ -100,13 +126,22 @@ private: void updateNodeTransform(Instance i) noexcept; void insertNode(Instance i, Instance p) noexcept; void swapNode(Instance i, Instance j) noexcept; - static void transformChildren(Sim& manager, Instance firstChild) noexcept; + void transformChildren(Sim& manager, Instance firstChild) noexcept; + + void computeAllWorldTransforms() noexcept; + + void computeWorldTransform(math::mat4f& outWorld, math::float3& inoutWorldTranslationLo, + math::mat4f const& pt, math::mat4f const& local, + math::float3 const& ptTranslationLo, math::float3 const& localTranslationLo, + bool accurate); friend class TransformManager::children_iterator; enum { LOCAL, // local transform (relative to parent), world if no parent WORLD, // world transform + LOCAL_LO, // accurate local translation + WORLD_LO, // accurate world translation PARENT, // instance to the parent FIRST_CHILD, // instance to our first child NEXT, // instance to our next sibling @@ -114,12 +149,14 @@ private: }; using Base = utils::SingleInstanceComponentManager< - math::mat4f, - math::mat4f, - Instance, - Instance, - Instance, - Instance + math::mat4f, // local + math::mat4f, // world + math::float3, // accurate local translation + math::float3, // accurate world translation + Instance, // parent + Instance, // firstChild + Instance, // next + Instance // prev >; struct Sim : public Base { @@ -138,6 +175,8 @@ private: // this specific usage of union is permitted. All fields are identical Field local; Field world; + Field localTranslationLo; + Field worldTranslationLo; Field parent; Field firstChild; Field next; @@ -155,6 +194,7 @@ private: Sim mManager; bool mLocalTransformTransactionOpen = false; + bool mAccurateTranslations = false; }; FILAMENT_UPCAST(TransformManager) diff --git a/samples/lightbulb.cpp b/samples/lightbulb.cpp index 138a1987c0..5c5d7359a2 100644 --- a/samples/lightbulb.cpp +++ b/samples/lightbulb.cpp @@ -337,7 +337,7 @@ static void setup(Engine* engine, View* view, Scene* scene) { .falloff(20.0f) .build(*engine, g_lights.back()); - tcm.create(g_lights.back(), parent, {}); + tcm.create(g_lights.back(), parent); scene->addEntity(g_lights.back()); }