From 1ceed1519b08b4cbb5804433aee4864dc48900b1 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Mon, 10 Jan 2022 14:00:53 -0800
Subject: [PATCH 01/22] attempt to fix compilation failure with gcc
fix #4990
---
filament/backend/include/backend/DriverEnums.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h
index b521f3a4e0..abd38cc77a 100644
--- a/filament/backend/include/backend/DriverEnums.h
+++ b/filament/backend/include/backend/DriverEnums.h
@@ -809,10 +809,10 @@ using AttributeArray = std::array;
//! Raster state descriptor
struct RasterState {
- using CullingMode = CullingMode;
- using DepthFunc = SamplerCompareFunc;
- using BlendEquation = BlendEquation;
- using BlendFunction = BlendFunction;
+ using CullingMode = backend::CullingMode;
+ using DepthFunc = backend::SamplerCompareFunc;
+ using BlendEquation = backend::BlendEquation;
+ using BlendFunction = backend::BlendFunction;
RasterState() noexcept { // NOLINT
static_assert(sizeof(RasterState) == sizeof(uint32_t),
From 5e0615040d36c50c3c4f6f359f1e8873ce7b8921 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Mon, 10 Jan 2022 11:59:14 -0800
Subject: [PATCH 02/22] refactor our ostream
The main change here is that we factorize all the interesting code in
a single function. This should result in smaller code.
---
libs/utils/include/utils/ostream.h | 31 +++---
libs/utils/src/ostream.cpp | 155 ++++++++++++-----------------
2 files changed, 80 insertions(+), 106 deletions(-)
diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h
index 4d1a9b9035..723f61f8a0 100644
--- a/libs/utils/include/utils/ostream.h
+++ b/libs/utils/include/utils/ostream.h
@@ -18,12 +18,12 @@
#define TNT_UTILS_OSTREAM_H
#include
+#include
#include
#include // ssize_t is a POSIX type.
-namespace utils {
-namespace io {
+namespace utils::io {
class UTILS_PUBLIC ostream {
public:
@@ -70,19 +70,26 @@ protected:
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
+ const char* get() const noexcept { return buffer; }
+
+ std::pair grow(size_t s) noexcept;
+ void advance(ssize_t n) noexcept;
+ void reset() noexcept;
+
+ private:
+ void reserve(size_t newSize) noexcept;
+
char* buffer = nullptr; // buffer address
char* curr = nullptr; // current pointer
size_t size = 0; // size remaining
size_t capacity = 0; // total capacity of the buffer
- const char* get() const noexcept { return buffer; }
- void advance(ssize_t n) noexcept;
- void reset() noexcept;
- void reserve(size_t newSize) noexcept;
};
Buffer mData;
Buffer& getBuffer() noexcept { return mData; }
+ ostream& print(const char* format, ...) noexcept;
+
private:
virtual ostream& flush() noexcept = 0;
@@ -96,13 +103,9 @@ private:
LONG_DOUBLE
};
- bool mShowHex = false;
- const char* getFormat(type t) const noexcept;
+ inline const char* getFormat(type t) const noexcept;
- /*
- * Checks that the buffer has room for s additional bytes, growing the allocation if necessary.
- */
- void growBufferIfNeeded(size_t s) noexcept;
+ bool mShowHex = false;
};
// handles std::string
@@ -129,8 +132,6 @@ inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
-} // namespace io
-
-} // namespace utils
+} // namespace utils::io
#endif // TNT_UTILS_OSTREAM_H
diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp
index 5eeaf67f2c..0e5c37a9b4 100644
--- a/libs/utils/src/ostream.cpp
+++ b/libs/utils/src/ostream.cpp
@@ -18,11 +18,10 @@
#include
#include
+#include
#include
-namespace utils {
-
-namespace io {
+namespace utils::io {
ostream::~ostream() = default;
@@ -35,7 +34,6 @@ ostream::Buffer::~Buffer() noexcept {
free(buffer);
}
-UTILS_NOINLINE
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t written = n < size ? size_t(n) : size;
@@ -44,7 +42,6 @@ void ostream::Buffer::advance(ssize_t n) noexcept {
}
}
-UTILS_NOINLINE
void ostream::Buffer::reserve(size_t newSize) noexcept {
size_t offset = curr - buffer;
if (buffer == nullptr) {
@@ -58,7 +55,6 @@ void ostream::Buffer::reserve(size_t newSize) noexcept {
size = capacity - offset;
}
-UTILS_NOINLINE
void ostream::Buffer::reset() noexcept {
// aggressively shrink the buffer
if (capacity > 1024) {
@@ -70,6 +66,17 @@ void ostream::Buffer::reset() noexcept {
size = capacity;
}
+
+std::pair ostream::Buffer::grow(size_t s) noexcept {
+ if (UTILS_UNLIKELY(size < s)) {
+ size_t used = curr - buffer;
+ size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
+ reserve(newCapacity);
+ assert(size >= s);
+ }
+ return { curr, size };
+}
+
const char* ostream::getFormat(ostream::type t) const noexcept {
switch (t) {
case type::SHORT: return mShowHex ? "0x%hx" : "%hd";
@@ -87,94 +94,80 @@ const char* ostream::getFormat(ostream::type t) const noexcept {
}
}
-void ostream::growBufferIfNeeded(size_t s) noexcept {
+UTILS_NOINLINE
+ostream& ostream::print(const char* format, ...) noexcept {
+ va_list args0;
+ va_list args1;
+
+ // figure out how much size to we need
+ va_start(args0, format);
+ va_copy(args1, args0);
+ ssize_t s = vsnprintf(nullptr, 0, format, args0);
+ va_end(args0);
+
+ // grow the buffer to the needed size
Buffer& buf = getBuffer();
- if (UTILS_UNLIKELY(buf.size < s)) {
- size_t used = buf.curr - buf.buffer;
- size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
- buf.reserve(newCapacity);
- assert(buf.size >= s);
- }
+ auto [curr, size] = buf.grow(s + 1); // +1 to include the null-terminator
+
+ // print into the buffer
+ vsnprintf(curr, size, format, args1);
+
+ // advance the buffer
+ buf.advance(s);
+
+ va_end(args1);
+
+ return *this;
}
ostream& ostream::operator<<(short value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::SHORT), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::SHORT), value));
- return *this;
+ const char* format = getFormat(type::SHORT);
+ return print(format, value);
}
ostream& ostream::operator<<(unsigned short value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::USHORT), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::USHORT), value));
- return *this;
+ const char* format = getFormat(type::USHORT);
+ return print(format, value);
}
ostream& ostream::operator<<(char value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::CHAR), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::CHAR), value));
- return *this;
+ const char* format = getFormat(type::CHAR);
+ return print(format, value);
}
ostream& ostream::operator<<(unsigned char value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::UCHAR), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UCHAR), value));
- return *this;
+ const char* format = getFormat(type::UCHAR);
+ return print(format, value);
}
ostream& ostream::operator<<(int value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::INT), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::INT), value));
- return *this;
+ const char* format = getFormat(type::INT);
+ return print(format, value);
}
ostream& ostream::operator<<(unsigned int value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::UINT), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UINT), value));
- return *this;
+ const char* format = getFormat(type::UINT);
+ return print(format, value);
}
ostream& ostream::operator<<(long value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::LONG), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG), value));
- return *this;
+ const char* format = getFormat(type::LONG);
+ return print(format, value);
}
ostream& ostream::operator<<(unsigned long value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::ULONG), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG), value));
- return *this;
+ const char* format = getFormat(type::ULONG);
+ return print(format, value);
}
ostream& ostream::operator<<(long long value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::LONG_LONG), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_LONG), value));
- return *this;
+ const char* format = getFormat(type::LONG_LONG);
+ return print(format, value);
}
ostream& ostream::operator<<(unsigned long long value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::ULONG_LONG), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG_LONG), value));
- return *this;
+ const char* format = getFormat(type::ULONG_LONG);
+ return print(format, value);
}
ostream& ostream::operator<<(float value) noexcept {
@@ -182,19 +175,13 @@ ostream& ostream::operator<<(float value) noexcept {
}
ostream& ostream::operator<<(double value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::DOUBLE), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::DOUBLE), value));
- return *this;
+ const char* format = getFormat(type::DOUBLE);
+ return print(format, value);
}
ostream& ostream::operator<<(long double value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, getFormat(type::LONG_DOUBLE), value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_DOUBLE), value));
- return *this;
+ const char* format = getFormat(type::LONG_DOUBLE);
+ return print(format, value);
}
ostream& ostream::operator<<(bool value) noexcept {
@@ -202,27 +189,15 @@ ostream& ostream::operator<<(bool value) noexcept {
}
ostream& ostream::operator<<(const char* string) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, "%s", string);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, "%s", string));
- return *this;
+ return print("%s", string);
}
ostream& ostream::operator<<(const unsigned char* string) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, "%s", string);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, "%s", string));
- return *this;
+ return print("%s", string);
}
ostream& ostream::operator<<(const void* value) noexcept {
- Buffer& buf = getBuffer();
- size_t s = snprintf(nullptr, 0, "%p", value);
- growBufferIfNeeded(s + 1); // +1 to include the null-terminator
- buf.advance(snprintf(buf.curr, buf.size, "%p", value));
- return *this;
+ return print("%p", value);
}
ostream& ostream::hex() noexcept {
@@ -235,6 +210,4 @@ ostream& ostream::dec() noexcept {
return *this;
}
-} // namespace io
-
-} // namespace utils
+} // namespace utils::io
From 047543a4a6fab6c32407a452e8413ccb075e9d03 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Mon, 10 Jan 2022 12:07:32 -0800
Subject: [PATCH 03/22] make our ostream thread safe
ostream internals are now protected by a lock, which ensures the
internal state stays consistant, however, this won't prevent
multiple threads to have their output ominterfering with each other,
which is no different from the stl behavior.
This fixes #4992
---
libs/utils/include/utils/ostream.h | 2 ++
libs/utils/src/Log.cpp | 9 +++++++++
libs/utils/src/ostream.cpp | 20 +++++++++++++-------
3 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h
index 723f61f8a0..1c5e26029d 100644
--- a/libs/utils/include/utils/ostream.h
+++ b/libs/utils/include/utils/ostream.h
@@ -17,6 +17,7 @@
#ifndef TNT_UTILS_OSTREAM_H
#define TNT_UTILS_OSTREAM_H
+#include
#include
#include
@@ -85,6 +86,7 @@ protected:
size_t capacity = 0; // total capacity of the buffer
};
+ std::mutex mLock;
Buffer mData;
Buffer& getBuffer() noexcept { return mData; }
diff --git a/libs/utils/src/Log.cpp b/libs/utils/src/Log.cpp
index 4d3234193d..321707672a 100644
--- a/libs/utils/src/Log.cpp
+++ b/libs/utils/src/Log.cpp
@@ -46,6 +46,7 @@ private:
};
ostream& LogStream::flush() noexcept {
+ std::lock_guard lock(mLock);
Buffer& buf = getBuffer();
#ifdef __ANDROID__
switch (mPriority) {
@@ -86,6 +87,14 @@ ostream& LogStream::flush() noexcept {
return *this;
}
+
+/*
+ * We can't use thread_local because on Android we're currently using several dynamic libraries
+ * including this .o (via libutils.a), which violates the ODR and ends-up with only one of
+ * the thread_local instance initialized.
+ * For this reason, ostream is protected by a mutex instead.
+ */
+
static LogStream cout(LogStream::Priority::LOG_DEBUG);
static LogStream cerr(LogStream::Priority::LOG_ERROR);
static LogStream cwarn(LogStream::Priority::LOG_WARNING);
diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp
index 0e5c37a9b4..ec154a8824 100644
--- a/libs/utils/src/ostream.cpp
+++ b/libs/utils/src/ostream.cpp
@@ -105,15 +105,21 @@ ostream& ostream::print(const char* format, ...) noexcept {
ssize_t s = vsnprintf(nullptr, 0, format, args0);
va_end(args0);
- // grow the buffer to the needed size
- Buffer& buf = getBuffer();
- auto [curr, size] = buf.grow(s + 1); // +1 to include the null-terminator
- // print into the buffer
- vsnprintf(curr, size, format, args1);
+ { // scope for the lock
+ std::lock_guard lock(mLock);
- // advance the buffer
- buf.advance(s);
+ Buffer& buf = getBuffer();
+
+ // grow the buffer to the needed size
+ auto[curr, size] = buf.grow(s + 1); // +1 to include the null-terminator
+
+ // print into the buffer
+ vsnprintf(curr, size, format, args1);
+
+ // advance the buffer
+ buf.advance(s);
+ }
va_end(args1);
From fb1eb35ba5557d161b5bc82a9f0979b3e2977942 Mon Sep 17 00:00:00 2001
From: Ankbzpx
Date: Tue, 11 Jan 2022 07:06:23 +0800
Subject: [PATCH 04/22] Fix aabb misalignment for skinned gltf model (#4973)
---
.../android/filament/utils/ModelViewer.kt | 3 +-
.../src/main/cpp/ResourceLoader.cpp | 5 +-
.../filament/gltfio/ResourceLoader.java | 9 +-
docs/remote/filament.js | 6 +-
docs/viewer/filament-viewer.js | 4 +-
docs/webgl/filament.js | 6 +-
libs/gltfio/include/gltfio/ResourceLoader.h | 4 +
libs/gltfio/src/ResourceLoader.cpp | 107 ++++++++++++++++--
samples/gltf_viewer.cpp | 32 ++++--
web/filament-js/extensions.js | 6 +-
web/filament-js/filament-viewer.js | 4 +-
web/filament-js/jsbindings.cpp | 5 +-
12 files changed, 150 insertions(+), 41 deletions(-)
diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
index b9eedd015b..a36c89501c 100644
--- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
+++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
@@ -71,6 +71,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
var normalizeSkinningWeights = true
var recomputeBoundingBoxes = false
+ var ignoreBindTransform = false
var cameraFocalLength = 28f
set(value) {
@@ -111,7 +112,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
view.camera = camera
assetLoader = AssetLoader(engine, UbershaderLoader(engine), EntityManager.get())
- resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes)
+ resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes, ignoreBindTransform)
// Always add a direct light source since it is required for shadowing.
// We highly recommend adding an indirect light as well.
diff --git a/android/gltfio-android/src/main/cpp/ResourceLoader.cpp b/android/gltfio-android/src/main/cpp/ResourceLoader.cpp
index 2795eb6e7f..c16ba604ed 100644
--- a/android/gltfio-android/src/main/cpp/ResourceLoader.cpp
+++ b/android/gltfio-android/src/main/cpp/ResourceLoader.cpp
@@ -35,10 +35,11 @@ static void destroy(void*, size_t, void *userData) {
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateResourceLoader(JNIEnv*, jclass,
- jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes) {
+ jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes,
+ jboolean ignoreBindTransform) {
Engine* engine = (Engine*) nativeEngine;
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights,
- (bool) recomputeBoundingBoxes });
+ (bool) recomputeBoundingBoxes, (bool) ignoreBindTransform});
}
extern "C" JNIEXPORT void JNICALL
diff --git a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/ResourceLoader.java b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/ResourceLoader.java
index bd39de2aaf..976a752caf 100644
--- a/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/ResourceLoader.java
+++ b/android/gltfio-android/src/main/java/com/google/android/filament/gltfio/ResourceLoader.java
@@ -45,7 +45,7 @@ public class ResourceLoader {
*/
public ResourceLoader(@NonNull Engine engine) {
long nativeEngine = engine.getNativeObject();
- mNativeObject = nCreateResourceLoader(nativeEngine, false, false);
+ mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
}
/**
@@ -54,14 +54,15 @@ public class ResourceLoader {
* @param engine the engine that gets passed to all builder methods
* @param normalizeSkinningWeights scale non-conformant skinning weights so they sum to 1
* @param recomputeBoundingBoxes use computed bounding boxes rather than the ones in the asset
+ * @param ignoreBindTransform ignore skinned primitives bind transform when compute bounding boxes
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights,
- boolean recomputeBoundingBoxes) {
+ boolean recomputeBoundingBoxes, boolean ignoreBindTransform) {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
- recomputeBoundingBoxes);
+ recomputeBoundingBoxes, ignoreBindTransform);
}
/**
@@ -166,7 +167,7 @@ public class ResourceLoader {
}
private static native long nCreateResourceLoader(long nativeEngine,
- boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes);
+ boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes, boolean ignoreBindTransform);
private static native void nDestroyResourceLoader(long nativeLoader);
private static native void nAddResourceData(long nativeLoader, String url, Buffer buffer,
int remaining);
diff --git a/docs/remote/filament.js b/docs/remote/filament.js
index e70870f6f4..e101956fdd 100644
--- a/docs/remote/filament.js
+++ b/docs/remote/filament.js
@@ -713,7 +713,8 @@ Filament.loadClassExtensions = function() {
const interval = asyncInterval || 30;
const defaults = {
normalizeSkinningWeights: true,
- recomputeBoundingBoxes: false
+ recomputeBoundingBoxes: false,
+ ignoreBindTransform: false
};
config = Object.assign(defaults, config || {});
@@ -736,7 +737,8 @@ Filament.loadClassExtensions = function() {
// Construct a resource loader and start decoding after all textures are fetched.
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes);
+ config.recomputeBoundingBoxes,
+ config.ignoreBindTransform);
const onComplete = () => {
resourceLoader.asyncBeginLoad(asset);
diff --git a/docs/viewer/filament-viewer.js b/docs/viewer/filament-viewer.js
index 594f6ca535..79f2f6b989 100644
--- a/docs/viewer/filament-viewer.js
+++ b/docs/viewer/filament-viewer.js
@@ -307,6 +307,7 @@ class FilamentViewer extends LitElement {
const config = {
normalizeSkinningWeights: true,
recomputeBoundingBoxes: false,
+ ignoreBindTransform: false,
asyncInterval: 30
};
@@ -333,7 +334,8 @@ class FilamentViewer extends LitElement {
const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes);
+ config.recomputeBoundingBoxes,
+ config.ignoreBindTransform);
let remaining = Object.keys(this.srcBlobResources).length;
for (const name in this.srcBlobResources) {
diff --git a/docs/webgl/filament.js b/docs/webgl/filament.js
index 1e65c86c3b..b2a6327518 100644
--- a/docs/webgl/filament.js
+++ b/docs/webgl/filament.js
@@ -713,7 +713,8 @@ Filament.loadClassExtensions = function() {
const interval = asyncInterval || 30;
const defaults = {
normalizeSkinningWeights: true,
- recomputeBoundingBoxes: false
+ recomputeBoundingBoxes: false,
+ ignoreBindTransform: false
};
config = Object.assign(defaults, config || {});
@@ -736,7 +737,8 @@ Filament.loadClassExtensions = function() {
// Construct a resource loader and start decoding after all textures are fetched.
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes);
+ config.recomputeBoundingBoxes,
+ config.ignoreBindTransform);
const onComplete = () => {
resourceLoader.asyncBeginLoad(asset);
diff --git a/libs/gltfio/include/gltfio/ResourceLoader.h b/libs/gltfio/include/gltfio/ResourceLoader.h
index b69a16e565..a92a4f8517 100644
--- a/libs/gltfio/include/gltfio/ResourceLoader.h
+++ b/libs/gltfio/include/gltfio/ResourceLoader.h
@@ -52,6 +52,10 @@ struct ResourceConfiguration {
//! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files
//! do not need this, but it is useful for robustness.
bool recomputeBoundingBoxes;
+
+ //! If true, ignore skinned primitives bind transform when compute bounding box. Implicitly true
+ //! for instanced asset. Only applicable when recomputeBoundingBoxes is set to true
+ bool ignoreBindTransform;
};
/**
diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp
index 179bea3e06..4d7142b9dd 100644
--- a/libs/gltfio/src/ResourceLoader.cpp
+++ b/libs/gltfio/src/ResourceLoader.cpp
@@ -86,13 +86,19 @@ struct ResourceLoader::Impl {
mEngine = config.engine;
mNormalizeSkinningWeights = config.normalizeSkinningWeights;
mRecomputeBoundingBoxes = config.recomputeBoundingBoxes;
+ mIgnoreBindTransform = config.ignoreBindTransform;
}
Engine* mEngine;
bool mNormalizeSkinningWeights;
bool mRecomputeBoundingBoxes;
+ bool mIgnoreBindTransform;
std::string mGltfPath;
+ // This is used to calculate skinIndex when updateBoundingBoxes, so that the correspondency between
+ // cgltf_node* and FFilamentInstance::Skin can be retrieved. This pointer doesn't need to be freed.
+ cgltf_skin* cgltfSkinBaseAddress;
+
// User-provided resource data with URI string keys, populated with addResourceData().
// This is used on platforms without traditional file systems, such as Android, iOS, and WebGL.
UriDataCache mUriDataCache;
@@ -458,6 +464,11 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
}
if (pImpl->mRecomputeBoundingBoxes) {
+ // asset->mSkins is unused for instanced assets
+ if (!pImpl->mIgnoreBindTransform) {
+ pImpl->mIgnoreBindTransform = asset->isInstanced();
+ }
+ pImpl->cgltfSkinBaseAddress = &gltf->skins[0];
updateBoundingBoxes(asset);
}
@@ -1005,27 +1016,99 @@ void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const {
*result = aabb;
};
- // Collect all mesh primitives that we wish to find bounds for.
- std::vector prims;
+ const size_t posAttrSize = cgltf_num_components(cgltf_type_vec3);
+ const size_t skinningAttrSize = cgltf_num_components(cgltf_type_vec4);
+ auto computeBoundingBoxSkinned = [&](const cgltf_primitive* prim, const Skin* skin, Aabb* result) {
+ Aabb aabb;
+ std::vector inverseGlobalTransforms(skin->targets.size());
+ for (size_t i = 0; i < skin->targets.size(); i++) {
+ auto xformable = tm.getInstance(skin->targets[i]);
+ if (xformable) {
+ inverseGlobalTransforms[i] = inverse(tm.getWorldTransform(xformable));
+ }
+ }
+ std::vector verts;
+ std::vector rawJoints;
+ std::vector weights;
+ for (cgltf_size slot = 0; slot < prim->attributes_count; slot++) {
+ const cgltf_attribute& attr = prim->attributes[slot];
+ const cgltf_accessor* accessor = attr.data;
+ if (attr.type == cgltf_attribute_type_position && cgltf_num_components(accessor->type) >= posAttrSize) {
+ verts.resize(accessor->count * posAttrSize);
+ cgltf_accessor_unpack_floats(accessor, &verts[0], accessor->count * posAttrSize);
+ }
+ if (attr.type == cgltf_attribute_type_joints && cgltf_num_components(accessor->type) >= skinningAttrSize) {
+ rawJoints.resize(accessor->count * skinningAttrSize);
+ cgltf_accessor_unpack_floats(accessor, &rawJoints[0], accessor->count * skinningAttrSize);
+ }
+ if (attr.type == cgltf_attribute_type_weights && cgltf_num_components(accessor->type) >= skinningAttrSize) {
+ weights.resize(accessor->count * skinningAttrSize);
+ cgltf_accessor_unpack_floats(accessor, &weights[0], accessor->count * skinningAttrSize);
+ }
+ }
+ std::vector jointIndices(rawJoints.begin(), rawJoints.end());
+ auto primitiveCount = static_cast(verts.size() / posAttrSize);
+ for (size_t i = 0; i < primitiveCount; i++) {
+ float3 point(verts[posAttrSize * i], verts[posAttrSize * i + 1], verts[posAttrSize * i + 2]);
+ mat4f tmp = mat4f(0.0f);
+ for (size_t j = 0; j < skinningAttrSize; j++) {
+ size_t jointIndex = jointIndices[skinningAttrSize * i + j];
+ float jointWeight = weights[skinningAttrSize * i + j];
+ Entity jointEntity = skin->joints[jointIndex];
+ mat4f globalJointTransform = tm.getWorldTransform(tm.getInstance(jointEntity));
+ mat4f inverseBindMatrix = skin->inverseBindMatrices[jointIndex];
+ tmp += jointWeight * globalJointTransform * inverseBindMatrix;
+ }
+ for (const auto& inverseGlobalTransform: inverseGlobalTransforms) {
+ mat4f skinMatrix = inverseGlobalTransform * tmp;
+ if (!pImpl->mNormalizeSkinningWeights) {
+ skinMatrix /= skinMatrix[3].w;
+ }
+ float3 skinnedPoint = (point.x * skinMatrix[0] + point.y * skinMatrix[1] + point.z * skinMatrix[2] + skinMatrix[3]).xyz;
+ aabb.min = min(aabb.min, skinnedPoint);
+ aabb.max = max(aabb.max, skinnedPoint);
+ }
+ }
+ *result = aabb;
+ };
+
+ // Collect all mesh primitives that we wish to find bounds for. For each mesh primitive, we also
+ // collect the skin it bound to (nullptr if not skinned) for bounds computation.
+ std::vector> primitives;
for (auto iter : nodeMap) {
- const cgltf_mesh* mesh = iter.first->mesh;
- if (mesh) {
- for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) {
- prims.push_back(&mesh->primitives[index]);
+ const Skin* skin = nullptr;
+ cgltf_skin* const cgltfSkin = iter.first->skin;
+ if (cgltfSkin) {
+ // importSkins unpacked cgltfSkin into FFilamentInstance::SkinVector bijectively so that
+ // the unpacked Skin can be retrieved given cgltfSkin index
+ int skinIndex = cgltfSkin - pImpl->cgltfSkinBaseAddress;
+ skin = &asset->mSkins[skinIndex];
+ }
+ const cgltf_mesh* cgltfMesh = iter.first->mesh;
+ if (cgltfMesh) {
+ for (cgltf_size index = 0, nprims = cgltfMesh->primitives_count; index < nprims; ++index) {
+ primitives.push_back({&cgltfMesh->primitives[index], skin});
}
}
}
// Kick off a bounding box job for every primitive.
- std::vector bounds(prims.size());
+ std::vector bounds(primitives.size());
JobSystem* js = &pImpl->mEngine->getJobSystem();
JobSystem::Job* parent = js->createJob();
- for (size_t i = 0; i < prims.size(); ++i) {
- cgltf_primitive const* prim = prims[i];
+ for (size_t i = 0; i < primitives.size(); ++i) {
Aabb* result = &bounds[i];
- js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] {
- computeBoundingBox(prim, result);
- }));
+ if (pImpl->mIgnoreBindTransform || !primitives[i].second) {
+ cgltf_primitive const* prim = primitives[i].first;
+ js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] {
+ computeBoundingBox(prim, result);
+ }));
+ } else {
+ std::pair skinnedPrimitive = primitives[i];
+ js->run(jobs::createJob(*js, parent, [skinnedPrimitive, result, computeBoundingBoxSkinned] {
+ computeBoundingBoxSkinned(skinnedPrimitive.first, skinnedPrimitive.second, result);
+ }));
+ }
}
js->runAndWait(parent);
diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp
index 60d886ebb5..140271ec56 100644
--- a/samples/gltf_viewer.cpp
+++ b/samples/gltf_viewer.cpp
@@ -85,6 +85,7 @@ struct App {
gltfio::ResourceLoader* resourceLoader = nullptr;
bool recomputeAabb = false;
+ bool ignoreBindTransform = false;
bool actualSize = false;
@@ -133,6 +134,8 @@ static void printUsage(char* name) {
" Do not scale the model to fit into a unit cube\n\n"
" --recompute-aabb, -r\n"
" Ignore the min/max attributes in the glTF file\n\n"
+ " --ignore-bind-transform, -g\n"
+ " Ignore bind transform when recomputing aabb\n\n"
" --settings=, -t\n"
" Apply the settings in the given JSON file\n\n"
" --ubershader, -u\n"
@@ -161,19 +164,20 @@ static std::ifstream::pos_type getFileSize(const char* filename) {
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
- static constexpr const char* OPTSTR = "ha:i:usc:rt:b:ev";
+ static constexpr const char* OPTSTR = "ha:i:usc:rgt:b:ev";
static const struct option OPTIONS[] = {
- { "help", no_argument, nullptr, 'h' },
- { "api", required_argument, nullptr, 'a' },
- { "batch", required_argument, nullptr, 'b' },
- { "headless", no_argument, nullptr, 'e' },
- { "ibl", required_argument, nullptr, 'i' },
- { "ubershader", no_argument, nullptr, 'u' },
- { "actual-size", no_argument, nullptr, 's' },
- { "camera", required_argument, nullptr, 'c' },
- { "recompute-aabb", no_argument, nullptr, 'r' },
- { "settings", required_argument, nullptr, 't' },
- { "split-view", no_argument, nullptr, 'v' },
+ { "help", no_argument, nullptr, 'h' },
+ { "api", required_argument, nullptr, 'a' },
+ { "batch", required_argument, nullptr, 'b' },
+ { "headless", no_argument, nullptr, 'e' },
+ { "ibl", required_argument, nullptr, 'i' },
+ { "ubershader", no_argument, nullptr, 'u' },
+ { "actual-size", no_argument, nullptr, 's' },
+ { "camera", required_argument, nullptr, 'c' },
+ { "recompute-aabb", no_argument, nullptr, 'r' },
+ { "ignore-bind-transform", no_argument, nullptr, 'g' },
+ { "settings", required_argument, nullptr, 't' },
+ { "split-view", no_argument, nullptr, 'v' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
@@ -220,6 +224,9 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
case 'r':
app->recomputeAabb = true;
break;
+ case 'g':
+ app->ignoreBindTransform = true;
+ break;
case 't':
app->settingsFile = arg;
break;
@@ -412,6 +419,7 @@ int main(int argc, char** argv) {
configuration.engine = app.engine;
configuration.gltfPath = gltfPath.c_str();
configuration.recomputeBoundingBoxes = app.recomputeAabb;
+ configuration.ignoreBindTransform = app.ignoreBindTransform;
configuration.normalizeSkinningWeights = true;
if (!app.resourceLoader) {
app.resourceLoader = new gltfio::ResourceLoader(configuration);
diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js
index 1684e11b75..346ff848dd 100644
--- a/web/filament-js/extensions.js
+++ b/web/filament-js/extensions.js
@@ -578,7 +578,8 @@ Filament.loadClassExtensions = function() {
const interval = asyncInterval || 30;
const defaults = {
normalizeSkinningWeights: true,
- recomputeBoundingBoxes: false
+ recomputeBoundingBoxes: false,
+ ignoreBindTransform: false
};
config = Object.assign(defaults, config || {});
@@ -601,7 +602,8 @@ Filament.loadClassExtensions = function() {
// Construct a resource loader and start decoding after all textures are fetched.
const resourceLoader = new Filament.gltfio$ResourceLoader(engine,
config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes);
+ config.recomputeBoundingBoxes,
+ config.ignoreBindTransform);
const onComplete = () => {
resourceLoader.asyncBeginLoad(asset);
diff --git a/web/filament-js/filament-viewer.js b/web/filament-js/filament-viewer.js
index 594f6ca535..79f2f6b989 100644
--- a/web/filament-js/filament-viewer.js
+++ b/web/filament-js/filament-viewer.js
@@ -307,6 +307,7 @@ class FilamentViewer extends LitElement {
const config = {
normalizeSkinningWeights: true,
recomputeBoundingBoxes: false,
+ ignoreBindTransform: false,
asyncInterval: 30
};
@@ -333,7 +334,8 @@ class FilamentViewer extends LitElement {
const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
config.normalizeSkinningWeights,
- config.recomputeBoundingBoxes);
+ config.recomputeBoundingBoxes,
+ config.ignoreBindTransform);
let remaining = Object.keys(this.srcBlobResources).length;
for (const name in this.srcBlobResources) {
diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp
index f2694ecd2b..1c29b48897 100644
--- a/web/filament-js/jsbindings.cpp
+++ b/web/filament-js/jsbindings.cpp
@@ -1910,12 +1910,13 @@ class_("gltfio$AssetLoader")
class_("gltfio$ResourceLoader")
.constructor(EMBIND_LAMBDA(ResourceLoader*, (Engine* engine, bool normalizeSkinningWeights,
- bool recomputeBoundingBoxes), {
+ bool recomputeBoundingBoxes, bool ignoreBindTransform), {
return new ResourceLoader({
.engine = engine,
.gltfPath = nullptr,
.normalizeSkinningWeights = normalizeSkinningWeights,
- .recomputeBoundingBoxes = recomputeBoundingBoxes
+ .recomputeBoundingBoxes = recomputeBoundingBoxes,
+ .ignoreBindTransform = ignoreBindTransform
});
}), allow_raw_pointers())
From b67963176bb7f0b9972774958acf37fa8b1e8559 Mon Sep 17 00:00:00 2001
From: Jared Fowler
Date: Wed, 5 Jan 2022 12:00:57 -0700
Subject: [PATCH 05/22] Moving UiHelper to the constructor which allows for
more composable control of the underlying views & surface
---
.../android/filament/utils/ModelViewer.kt | 30 +++++++++++++------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
index a36c89501c..f55d0100ba 100644
--- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
+++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt
@@ -58,7 +58,10 @@ private const val kSensitivity = 100f
*
* See `sample-gltf-viewer` for a usage example.
*/
-class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
+class ModelViewer(
+ val engine: Engine,
+ private val uiHelper: UiHelper
+) : android.view.View.OnTouchListener {
var asset: FilamentAsset? = null
private set
@@ -85,7 +88,6 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
val renderer: Renderer
@Entity val light: Int
- private val uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
private lateinit var displayHelper: DisplayHelper
private lateinit var cameraManipulator: Manipulator
private lateinit var gestureDetector: GestureDetector
@@ -130,7 +132,12 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
scene.addEntity(light)
}
- constructor(surfaceView: SurfaceView, engine: Engine = Engine.create(), manipulator: Manipulator? = null) : this(engine) {
+ constructor(
+ surfaceView: SurfaceView,
+ engine: Engine = Engine.create(),
+ uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK),
+ manipulator: Manipulator? = null
+ ) : this(engine, uiHelper) {
cameraManipulator = manipulator ?: Manipulator.Builder()
.targetPosition(kDefaultObjectPosition.x, kDefaultObjectPosition.y, kDefaultObjectPosition.z)
.viewport(surfaceView.width, surfaceView.height)
@@ -145,7 +152,12 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
}
@Suppress("unused")
- constructor(textureView: TextureView, engine: Engine = Engine.create(), manipulator: Manipulator? = null) : this(engine) {
+ constructor(
+ textureView: TextureView,
+ engine: Engine = Engine.create(),
+ uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK),
+ manipulator: Manipulator? = null
+ ) : this(engine, uiHelper) {
cameraManipulator = manipulator ?: Manipulator.Builder()
.targetPosition(kDefaultObjectPosition.x, kDefaultObjectPosition.y, kDefaultObjectPosition.z)
.viewport(textureView.width, textureView.height)
@@ -216,8 +228,8 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
fun transformToUnitCube(centerPoint: Float3 = kDefaultObjectPosition) {
asset?.let { asset ->
val tm = engine.transformManager
- var center = asset.boundingBox.center.let { v-> Float3(v[0], v[1], v[2]) }
- val halfExtent = asset.boundingBox.halfExtent.let { v-> Float3(v[0], v[1], v[2]) }
+ var center = asset.boundingBox.center.let { v -> Float3(v[0], v[1], v[2]) }
+ val halfExtent = asset.boundingBox.halfExtent.let { v -> Float3(v[0], v[1], v[2]) }
val maxExtent = 2.0f * max(halfExtent)
val scaleFactor = 2.0f / maxExtent
center -= centerPoint / scaleFactor
@@ -285,9 +297,9 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
private fun populateScene(asset: FilamentAsset) {
val rcm = engine.renderableManager
var count = 0
- val popRenderables = {count = asset.popRenderables(readyRenderables); count != 0}
+ val popRenderables = { count = asset.popRenderables(readyRenderables); count != 0 }
while (popRenderables()) {
- for (i in 0..count-1) {
+ for (i in 0..count - 1) {
val ri = rcm.getInstance(readyRenderables[i])
rcm.setScreenSpaceContactShadows(ri, true)
}
@@ -384,4 +396,4 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener {
companion object {
private val kDefaultObjectPosition = Float3(0.0f, 0.0f, -4.0f)
}
-}
+}
\ No newline at end of file
From 6465b9ade8d8e7b2b9627a1a5b60bb9fa786c592 Mon Sep 17 00:00:00 2001
From: hzzengxiaoqi
Date: Fri, 7 Jan 2022 11:50:10 +0800
Subject: [PATCH 06/22] fix bug of GLTFIO_LITE, the macro
GLTFRESOURCES_LITE_LIT_BLEND_DATA is not defined anywhere, it should be
GLTFRESOURCES_LITE_LIT_FADE_DATA. This solves the issue of rendering
transparent objects using GLTFIO_LITE macro, i.e. using
gltfresources_lite.bin. If not change this macro, the transparent objects
will be rended using LIT_OPAQUE material instead of LIT_FADE material.
---
libs/gltfio/src/UbershaderLoader.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/gltfio/src/UbershaderLoader.cpp b/libs/gltfio/src/UbershaderLoader.cpp
index c7874462c1..b9323b2b88 100644
--- a/libs/gltfio/src/UbershaderLoader.cpp
+++ b/libs/gltfio/src/UbershaderLoader.cpp
@@ -131,7 +131,7 @@ Material* UbershaderLoader::getMaterial(const MaterialKey& config) const {
case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_OPAQUE); break;
#endif
- #if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_BLEND_DATA)
+ #if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_FADE_DATA)
case MATINDEX(LIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_FADE); break;
#endif
From 954f9112e43f8aacab36e24e12a58a08909a9a59 Mon Sep 17 00:00:00 2001
From: Romain Guy
Date: Wed, 12 Jan 2022 09:55:35 -0800
Subject: [PATCH 07/22] Replace generic tonemap curve with a simpler one
(#5041)
* Replace generic tonemap curve with a simpler one
This change removes the shoulder parameter which had inconsistent
behaviors. The new curve is simpler and designed to still match
by default the gray point and the contrast of ACES in a bright
surround.
* Fix build
---
.../src/main/cpp/ToneMapper.cpp | 15 +----
.../google/android/filament/ToneMapper.java | 28 ++------
filament/include/filament/ToneMapper.h | 12 +---
filament/src/ColorGrading.cpp | 10 +--
filament/src/ColorSpace.h | 58 ++++++++++++-----
filament/src/ToneMapper.cpp | 65 +++++--------------
libs/math/include/math/TVecHelpers.h | 7 ++
libs/viewer/include/viewer/Settings.h | 5 +-
libs/viewer/src/Settings.cpp | 9 +--
libs/viewer/src/SimpleViewer.cpp | 2 -
libs/viewer/tests/test_settings.cpp | 1 -
11 files changed, 82 insertions(+), 130 deletions(-)
diff --git a/android/filament-android/src/main/cpp/ToneMapper.cpp b/android/filament-android/src/main/cpp/ToneMapper.cpp
index 9318d450fd..ea040538cd 100644
--- a/android/filament-android/src/main/cpp/ToneMapper.cpp
+++ b/android/filament-android/src/main/cpp/ToneMapper.cpp
@@ -49,8 +49,8 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreateGenericToneMapper(JNIEnv*, jclass,
- jfloat contrast, jfloat shoulder, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) {
- return (jlong) new GenericToneMapper(contrast, shoulder, midGrayIn, midGrayOut, hdrMax);
+ jfloat contrast, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) {
+ return (jlong) new GenericToneMapper(contrast, midGrayIn, midGrayOut, hdrMax);
}
extern "C" JNIEXPORT jfloat JNICALL
@@ -58,11 +58,6 @@ Java_com_google_android_filament_ToneMapper_nGenericGetContrast(JNIEnv*, jclass,
return ((GenericToneMapper*) nativeObject)->getContrast();
}
-extern "C" JNIEXPORT jfloat JNICALL
-Java_com_google_android_filament_ToneMapper_nGenericGetShoulder(JNIEnv*, jclass, jlong nativeObject) {
- return ((GenericToneMapper*) nativeObject)->getShoulder();
-}
-
extern "C" JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_ToneMapper_nGenericGetMidGrayIn(JNIEnv*, jclass, jlong nativeObject) {
return ((GenericToneMapper*) nativeObject)->getMidGrayIn();
@@ -84,12 +79,6 @@ Java_com_google_android_filament_ToneMapper_nGenericSetContrast(JNIEnv*, jclass,
((GenericToneMapper*) nativeObject)->setContrast(contrast);
}
-extern "C" JNIEXPORT void JNICALL
-Java_com_google_android_filament_ToneMapper_nGenericSetShoulder(JNIEnv*, jclass,
- jlong nativeObject, jfloat shoulder) {
- ((GenericToneMapper*) nativeObject)->setShoulder(shoulder);
-}
-
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_ToneMapper_nGenericSetMidGrayIn(JNIEnv*, jclass,
jlong nativeObject, jfloat midGrayIn) {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
index 71264c2d49..15800562e3 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
@@ -109,7 +109,6 @@ public class ToneMapper {
* The tone mapping curve is defined by 5 parameters:
*
* - contrast: controls the contrast of the curve
- * - shoulder: controls the shoulder of the curve, i.e. how quickly scene
* referred values map to output white
* - midGrayIn: sets the input middle gray
* - midGrayOut: sets the output middle gray
@@ -123,15 +122,14 @@ public class ToneMapper {
* the {@link ACESLegacy} tone mapper. The default values are:
*
*
- * - contrast = 1.585f
- * - shoulder = 0.5f
+ * - contrast = 1.55f
* - midGrayIn = 0.18f
- * - midGrayOut = 0.268f
+ * - midGrayOut = 0.215f
* - hdrMax = 10.0f
*
*/
public Generic() {
- this(1.585f, 0.5f, 0.18f, 0.268f, 10.0f);
+ this(1.55f, 0.18f, 0.215f, 10.0f);
}
/**
@@ -139,16 +137,14 @@ public class ToneMapper {
*
* @param contrast: controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
- * @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
- * referred values map to output white, between 0.0 and 1.0.
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax: defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
*/
public Generic(
- float contrast, float shoulder, float midGrayIn, float midGrayOut, float hdrMax) {
- super(nCreateGenericToneMapper(contrast, shoulder, midGrayIn, midGrayOut, hdrMax));
+ float contrast, float midGrayIn, float midGrayOut, float hdrMax) {
+ super(nCreateGenericToneMapper(contrast, midGrayIn, midGrayOut, hdrMax));
}
/** Returns the contrast of the curve as a strictly positive value. */
@@ -161,16 +157,6 @@ public class ToneMapper {
nGenericSetContrast(getNativeObject(), contrast);
}
- /** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */
- public float getShoulder() {
- return nGenericGetShoulder(getNativeObject());
- }
-
- /** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
- public void setShoulder(float shoulder) {
- nGenericSetShoulder(getNativeObject(), shoulder);
- }
-
/** Returns the middle gray point for input values as a value between 0.0 and 1.0. */
public float getMidGrayIn() {
return nGenericGetMidGrayIn(getNativeObject());
@@ -209,17 +195,15 @@ public class ToneMapper {
private static native long nCreateACESLegacyToneMapper();
private static native long nCreateFilmicToneMapper();
private static native long nCreateGenericToneMapper(
- float contrast, float shoulder, float midGrayIn, float midGrayOut, float hdrMax);
+ float contrast, float midGrayIn, float midGrayOut, float hdrMax);
// Generic tone mappper
private static native float nGenericGetContrast(long nativeObject);
- private static native float nGenericGetShoulder(long nativeObject);
private static native float nGenericGetMidGrayIn(long nativeObject);
private static native float nGenericGetMidGrayOut(long nativeObject);
private static native float nGenericGetHdrMax(long nativeObject);
private static native void nGenericSetContrast(long nativeObject, float contrast);
- private static native void nGenericSetShoulder(long nativeObject, float shoulder);
private static native void nGenericSetMidGrayIn(long nativeObject, float midGrayIn);
private static native void nGenericSetMidGrayOut(long nativeObject, float midGrayOut);
private static native void nGenericSetHdrMax(long nativeObject, float hdrMax);
diff --git a/filament/include/filament/ToneMapper.h b/filament/include/filament/ToneMapper.h
index c0aa38d153..0bbe06fe3c 100644
--- a/filament/include/filament/ToneMapper.h
+++ b/filament/include/filament/ToneMapper.h
@@ -123,8 +123,6 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
*
* The tone mapping curve is defined by 5 parameters:
* - contrast: controls the contrast of the curve
- * - shoulder: controls the shoulder of the curve, i.e. how quickly scene
- * referred values map to output white
* - midGrayIn: sets the input middle gray
* - midGrayOut: sets the output middle gray
* - hdrMax: defines the maximum input value that will be mapped to
@@ -138,18 +136,15 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
*
* @param contrast: controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
- * @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
- * referred values map to output white, between 0.0 and 1.0.
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax: defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
*/
explicit GenericToneMapper(
- float contrast = 1.585f,
- float shoulder = 0.5f,
+ float contrast = 1.55f,
float midGrayIn = 0.18f,
- float midGrayOut = 0.268f,
+ float midGrayOut = 0.215f,
float hdrMax = 10.0f
) noexcept;
~GenericToneMapper() noexcept final;
@@ -179,9 +174,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
/** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */
void setContrast(float contrast) noexcept;
- /** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
- void setShoulder(float shoulder) noexcept;
-
/** Sets the input middle gray, between 0.0 and 1.0. */
void setMidGrayIn(float midGrayIn) noexcept;
diff --git a/filament/src/ColorGrading.cpp b/filament/src/ColorGrading.cpp
index 67ae43a274..ba86dbfbd8 100644
--- a/filament/src/ColorGrading.cpp
+++ b/filament/src/ColorGrading.cpp
@@ -400,7 +400,7 @@ constexpr mat3f adaptationTransform(float2 whiteBalance) noexcept {
float y = chromaticityCoordinateIlluminantD(x) + t * 0.066f;
float3 lms = XYZ_to_CIECAT16 * xyY_to_XYZ({x, y, 1.0f});
- return LMS_CAT16_to_REC2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * REC2020_to_LMS_CAT16;
+ return LMS_CAT16_to_Rec2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * Rec2020_to_LMS_CAT16;
}
UTILS_ALWAYS_INLINE
@@ -575,21 +575,21 @@ static mat3f selectColorGradingTransformIn(ColorGrading::ToneMapping toneMapping
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
return mat3f{};
}
- return sRGB_to_REC2020;
+ return sRGB_to_Rec2020;
}
static mat3f selectColorGradingTransformOut(ColorGrading::ToneMapping toneMapping) noexcept {
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
return mat3f{};
}
- return REC2020_to_sRGB;
+ return Rec2020_to_sRGB;
}
static float3 selectColorGradingLuminance(ColorGrading::ToneMapping toneMapping) noexcept {
if (toneMapping == ColorGrading::ToneMapping::FILMIC) {
- return LUMINANCE_REC709;
+ return LUMINANCE_Rec709;
}
- return LUMINANCE_REC2020;
+ return LUMINANCE_Rec2020;
}
#pragma clang diagnostic pop
diff --git a/filament/src/ColorSpace.h b/filament/src/ColorSpace.h
index 2e73f3e461..3ab62a70f1 100644
--- a/filament/src/ColorSpace.h
+++ b/filament/src/ColorSpace.h
@@ -52,13 +52,13 @@ constexpr mat3f sRGB_to_XYZ{
0.1804380f, 0.0721750f, 0.9503040f
};
-constexpr mat3f REC2020_to_XYZ{
+constexpr mat3f Rec2020_to_XYZ{
0.6369530f, 0.2626983f, 0.0000000f,
0.1446169f, 0.6780088f, 0.0280731f,
0.1688558f, 0.0592929f, 1.0608272f
};
-constexpr mat3f XYZ_to_REC2020{
+constexpr mat3f XYZ_to_Rec2020{
1.7166634f, -0.6666738f, 0.0176425f,
-0.3556733f, 1.6164557f, -0.0427770f,
-0.2533681f, 0.0157683f, 0.9422433f
@@ -130,6 +130,18 @@ constexpr mat3f sRGB_to_OkLab_LMS{
0.0514459929f, 0.1073969566f, 0.6299787005f
};
+constexpr mat3f XYZ_to_OkLab_LMS{
+ 0.8189330101f, 0.3618667424f, -0.1288597137f,
+ 0.0329845436f, 0.9293118715f, 0.0361456387f,
+ 0.0482003018f, 0.2643662691f, 0.6338517070f
+};
+
+constexpr mat3f OkLab_LMS_to_XYZ{
+ 1.227014f, -0.557800f, 0.281256f,
+ -0.040580f, 1.112257f, -0.071677f,
+ -0.076381f, -0.421482f, 1.586163f
+};
+
constexpr mat3f OkLab_LMS_to_OkLab{
0.2104542553f, 1.9779984951f, 0.0259040371f,
0.7936177850f, -2.4285922050f, 0.7827717662f,
@@ -148,21 +160,25 @@ constexpr mat3f OkLab_LMS_to_sRGB{
0.2309699292f, -0.3413193965f, 1.7076147010f
};
-constexpr mat3f sRGB_to_REC2020 = XYZ_to_REC2020 * sRGB_to_XYZ;
+constexpr mat3f sRGB_to_Rec2020 = XYZ_to_Rec2020 * sRGB_to_XYZ;
-constexpr mat3f REC2020_to_sRGB = XYZ_to_sRGB * REC2020_to_XYZ;
+constexpr mat3f Rec2020_to_sRGB = XYZ_to_sRGB * Rec2020_to_XYZ;
constexpr mat3f sRGB_to_LMS_CAT16 = XYZ_to_CIECAT16 * sRGB_to_XYZ;
constexpr mat3f LMS_CAT16_to_sRGB = XYZ_to_sRGB * CIECAT16_to_XYZ;
-constexpr mat3f REC2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * REC2020_to_XYZ;
+constexpr mat3f Rec2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * Rec2020_to_XYZ;
-constexpr mat3f LMS_CAT16_to_REC2020 = XYZ_to_REC2020 * CIECAT16_to_XYZ;
+constexpr mat3f LMS_CAT16_to_Rec2020 = XYZ_to_Rec2020 * CIECAT16_to_XYZ;
-constexpr mat3f REC2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * REC2020_to_XYZ;
+constexpr mat3f Rec2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * Rec2020_to_XYZ;
-constexpr mat3f AP1_to_REC2020 = XYZ_to_REC2020 * AP1_to_XYZ;
+constexpr mat3f AP1_to_Rec2020 = XYZ_to_Rec2020 * AP1_to_XYZ;
+
+constexpr mat3f Rec2020_to_OkLab_LMS = XYZ_to_OkLab_LMS * Rec2020_to_XYZ;
+
+constexpr mat3f OkLab_LMS_to_Rec2020 = XYZ_to_Rec2020 * OkLab_LMS_to_XYZ;
//------------------------------------------------------------------------------
// Constants
@@ -175,17 +191,17 @@ constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f};
// Result of: XYZ_to_CIECAT16 * xyY_to_XYZ(ILLUMINANT_D65_xyY);
constexpr float3 ILLUMINANT_D65_LMS_CAT16{0.975533f, 1.016483f, 1.084837f};
-// RGB to luminance coefficients for Rec.2020, from REC2020_to_XYZ
-constexpr float3 LUMINANCE_REC2020{0.2627002f, 0.6779981f, 0.0593017f};
+// RGB to luminance coefficients for Rec.2020, from Rec2020_to_XYZ
+constexpr float3 LUMINANCE_Rec2020{0.2627002f, 0.6779981f, 0.0593017f};
// RGB to luminance coefficients for ACEScg (AP1), from AP1_to_XYZ
constexpr float3 LUMINANCE_AP1{0.272229f, 0.674082f, 0.0536895f};
// RGB to luminance coefficients for Rec.709, from sRGB_to_XYZ
-constexpr float3 LUMINANCE_REC709{0.2126730f, 0.7151520f, 0.0721750f};
+constexpr float3 LUMINANCE_Rec709{0.2126730f, 0.7151520f, 0.0721750f};
// RGB to luminance coefficients for Rec.709 with HK-like weighting
-constexpr float3 LUMINANCE_HK_REC709{0.13913043f, 0.73043478f, 0.13043478f};
+constexpr float3 LUMINANCE_HK_Rec709{0.13913043f, 0.73043478f, 0.13043478f};
constexpr float MIDDLE_GRAY_ACEScg = 0.18f;
@@ -212,21 +228,29 @@ inline constexpr XYZ xyY_to_XYZ(xyY v) noexcept {
}
inline constexpr xyY XYZ_to_xyY(XYZ v) noexcept {
- return float3(v.xy / max(v.x + v.y + v.z, 1e-5f), v.y);
-}
-
-inline float3 sRGB_to_OkLab(float3 x) noexcept {
- return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x);
+ return {v.xy / max(v.x + v.y + v.z, 1e-5f), v.y};
}
inline constexpr float3 pow3(float3 x) noexcept {
return x * x * x;
}
+inline float3 sRGB_to_OkLab(float3 x) noexcept {
+ return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x);
+}
+
+inline float3 Rec2020_to_OkLab(float3 x) noexcept {
+ return OkLab_LMS_to_OkLab * cbrt(Rec2020_to_OkLab_LMS * x);
+}
+
inline float3 OkLab_to_sRGB(float3 x) noexcept {
return OkLab_LMS_to_sRGB * pow3(OkLab_to_OkLab_LMS * x);
}
+inline float3 OkLab_to_Rec2020(float3 x) noexcept {
+ return OkLab_LMS_to_Rec2020 * pow3(OkLab_to_OkLab_LMS * x);
+}
+
//------------------------------------------------------------------------------
// Conversion functions and encoding/decoding
//------------------------------------------------------------------------------
diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp
index cae64d9acd..75cbdd74b8 100644
--- a/filament/src/ToneMapper.cpp
+++ b/filament/src/ToneMapper.cpp
@@ -111,7 +111,7 @@ inline float3 darkSurround_to_dimSurround(float3 linearCV) {
float3 XYZ = AP1_to_XYZ * linearCV;
float3 xyY = XYZ_to_xyY(XYZ);
- xyY.z = clamp(xyY.z, 0.0f, (float)std::numeric_limits::max());
+ xyY.z = clamp(xyY.z, 0.0f, (float) std::numeric_limits::max());
xyY.z = std::pow(xyY.z, DIM_SURROUND_GAMMA);
XYZ = xyY_to_XYZ(xyY);
@@ -135,7 +135,7 @@ float3 ACES(float3 color, float brightness) noexcept {
constexpr float RRT_SAT_FACTOR = 0.96f;
constexpr float ODT_SAT_FACTOR = 0.93f;
- float3 ap0 = REC2020_to_AP0 * color;
+ float3 ap0 = Rec2020_to_AP0 * color;
// Glow module
float saturation = rgb_2_saturation(ap0);
@@ -177,7 +177,7 @@ float3 ACES(float3 color, float brightness) noexcept {
// Apply desaturation to compensate for luminance difference
linearCV = mix(float3(dot(linearCV, LUMINANCE_AP1)), linearCV, ODT_SAT_FACTOR);
- return AP1_to_REC2020 * linearCV;
+ return AP1_to_Rec2020 * linearCV;
}
} // namespace aces
@@ -261,7 +261,7 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept {
// The 5th color in the array (cyan) represents middle gray (18%)
// Every stop above or below middle gray causes a color shift
// TODO: This should depend on the working color grading color space
- float v = log2(dot(c, LUMINANCE_REC2020) / 0.18f);
+ float v = log2(dot(c, LUMINANCE_Rec2020) / 0.18f);
v = clamp(v + 5.0f, 0.0f, 15.0f);
size_t index = size_t(v);
@@ -272,71 +272,52 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept {
// Generic tone mapper
//------------------------------------------------------------------------------
-// Lottes, 2016,"Advanced Techniques and Optimization of VDR Color Pipelines":
-// https://gpuopen.com/wp-content/uploads/2016/03/GdcVdrLottes.pdf
-// Includes fix from Bart Wronski:
-// https://bartwronski.com/2016/09/01/dynamic-range-and-evs/
-
struct GenericToneMapper::Options {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
void setParameters(
float contrast,
- float shoulder,
float midGrayIn,
float midGrayOut,
float hdrMax
) {
contrast = max(contrast, 1e-5f);
- shoulder = saturate(shoulder);
midGrayIn = clamp(midGrayIn, 1e-5f, 1.0f);
midGrayOut = clamp(midGrayOut, 1e-5f, 1.0f);
hdrMax = max(hdrMax, 1.0f);
this->contrast = contrast;
- this->shoulder = shoulder;
this->midGrayIn = midGrayIn;
this->midGrayOut = midGrayOut;
this->hdrMax = hdrMax;
- // remap shoulder
- d = 0.8f + 0.4f * shoulder;
+ float a = pow(midGrayIn, contrast);
+ float b = pow(hdrMax, contrast);
+ float c = a - midGrayOut * b;
- float mc = std::pow(midGrayIn, contrast);
- float mcs = std::pow(mc, d);
-
- float hc = std::pow(hdrMax, contrast);
- float hcs = std::pow(hc, d);
-
- float u = (hcs - mcs) * midGrayOut;
- float v = mcs * midGrayOut;
-
- b = -((-mc + (midGrayOut * (hcs * mc - hc * v)) / u) / v);
- c = (hcs * mc - hc * v) / u;
+ inputScale = (a * b * (midGrayOut - 1.0f)) / c;
+ outputScale = midGrayOut * (a - b) / c;
}
#pragma clang diagnostic pop
float contrast;
- float shoulder;
float midGrayIn;
float midGrayOut;
float hdrMax;
- // Computed fields, do not modify
- float b;
- float c;
- float d;
+ // TEMP
+ float inputScale;
+ float outputScale;
};
GenericToneMapper::GenericToneMapper(
float contrast,
- float shoulder,
float midGrayIn,
float midGrayOut,
float hdrMax
) noexcept {
mOptions = new Options();
- mOptions->setParameters(contrast, shoulder, midGrayIn, midGrayOut, hdrMax);
+ mOptions->setParameters(contrast, midGrayIn, midGrayOut, hdrMax);
}
GenericToneMapper::~GenericToneMapper() noexcept {
@@ -354,12 +335,11 @@ GenericToneMapper& GenericToneMapper::operator=(GenericToneMapper&& rhs) noexcep
}
float3 GenericToneMapper::operator()(math::float3 x) const noexcept {
- float3 xc = pow(clamp(x, 0.0f, mOptions->hdrMax), mOptions->contrast);
- return saturate(xc / (pow(xc, mOptions->d) * mOptions->b + mOptions->c));
+ x = pow(x, mOptions->contrast);
+ return mOptions->outputScale * x / (x + mOptions->inputScale);
}
float GenericToneMapper::getContrast() const noexcept { return mOptions->contrast; }
-float GenericToneMapper::getShoulder() const noexcept { return mOptions->shoulder; }
float GenericToneMapper::getMidGrayIn() const noexcept { return mOptions->midGrayIn; }
float GenericToneMapper::getMidGrayOut() const noexcept { return mOptions->midGrayOut; }
float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax; }
@@ -367,27 +347,14 @@ float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax;
void GenericToneMapper::setContrast(float contrast) noexcept {
mOptions->setParameters(
contrast,
- mOptions->shoulder,
mOptions->midGrayIn,
mOptions->midGrayOut,
mOptions->hdrMax
);
}
-
-void GenericToneMapper::setShoulder(float shoulder) noexcept {
- mOptions->setParameters(
- mOptions->contrast,
- shoulder,
- mOptions->midGrayIn,
- mOptions->midGrayOut,
- mOptions->hdrMax
- );
-}
-
void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept {
mOptions->setParameters(
mOptions->contrast,
- mOptions->shoulder,
midGrayIn,
mOptions->midGrayOut,
mOptions->hdrMax
@@ -397,7 +364,6 @@ void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept {
void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept {
mOptions->setParameters(
mOptions->contrast,
- mOptions->shoulder,
mOptions->midGrayIn,
midGrayOut,
mOptions->hdrMax
@@ -407,7 +373,6 @@ void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept {
void GenericToneMapper::setHdrMax(float hdrMax) noexcept {
mOptions->setParameters(
mOptions->contrast,
- mOptions->shoulder,
mOptions->midGrayIn,
mOptions->midGrayOut,
hdrMax
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 43b346381b..f213dc714d 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -477,6 +477,13 @@ private:
return v;
}
+ friend inline VECTOR MATH_PURE sign(VECTOR v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::copysign(T(1), v[i]);
+ }
+ return v;
+ }
+
friend inline VECTOR MATH_PURE pow(VECTOR v, T p) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::pow(v[i], p);
diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h
index c17cbedbb5..272f213400 100644
--- a/libs/viewer/include/viewer/Settings.h
+++ b/libs/viewer/include/viewer/Settings.h
@@ -105,10 +105,9 @@ private:
};
struct GenericToneMapperSettings {
- float contrast = 1.585f;
- float shoulder = 0.5f;
+ float contrast = 1.55f;
float midGrayIn = 0.18f;
- float midGrayOut = 0.268f;
+ float midGrayOut = 0.215f;
float hdrMax = 10.0f;
bool operator!=(const GenericToneMapperSettings &rhs) const { return !(rhs == *this); }
bool operator==(const GenericToneMapperSettings &rhs) const;
diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp
index 5f4621aa68..758aceae41 100644
--- a/libs/viewer/src/Settings.cpp
+++ b/libs/viewer/src/Settings.cpp
@@ -336,8 +336,6 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, GenericT
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "contrast") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->contrast);
- } else if (compare(tok, jsonChunk, "shoulder") == 0) {
- i = parse(tokens, i + 1, jsonChunk, &out->shoulder);
} else if (compare(tok, jsonChunk, "midGrayIn") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->midGrayIn);
} else if (compare(tok, jsonChunk, "midGrayOut") == 0) {
@@ -1101,7 +1099,6 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe
case ToneMapping::FILMIC: return new FilmicToneMapper;
case ToneMapping::GENERIC: return new GenericToneMapper(
settings.genericToneMapper.contrast,
- settings.genericToneMapper.shoulder,
settings.genericToneMapper.midGrayIn,
settings.genericToneMapper.midGrayOut,
settings.genericToneMapper.hdrMax
@@ -1265,7 +1262,6 @@ static std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOpt
static std::ostream& operator<<(std::ostream& out, const GenericToneMapperSettings& in) {
return out << "{\n"
<< "\"contrast\": " << (in.contrast) << ",\n"
- << "\"shoulder\": " << (in.shoulder) << ",\n"
<< "\"midGrayIn\": " << (in.midGrayIn) << ",\n"
<< "\"midGrayOut\": " << (in.midGrayOut) << ",\n"
<< "\"hdrMax\": " << (in.hdrMax) << "\n"
@@ -1544,9 +1540,8 @@ static std::ostream& operator<<(std::ostream& out, const Settings& in) {
}
bool GenericToneMapperSettings::operator==(const GenericToneMapperSettings &rhs) const {
- static_assert(sizeof(GenericToneMapperSettings) == 20, "Please update Settings.cpp");
+ static_assert(sizeof(GenericToneMapperSettings) == 16, "Please update Settings.cpp");
return contrast == rhs.contrast &&
- shoulder == rhs.shoulder &&
midGrayIn == rhs.midGrayIn &&
midGrayOut == rhs.midGrayOut &&
hdrMax == rhs.hdrMax;
@@ -1555,7 +1550,7 @@ bool GenericToneMapperSettings::operator==(const GenericToneMapperSettings &rhs)
bool ColorGradingSettings::operator==(const ColorGradingSettings &rhs) const {
// If you had to fix the following codeline, then you likely also need to update the
// implementation of operator==.
- static_assert(sizeof(ColorGradingSettings) == 232, "Please update Settings.cpp");
+ static_assert(sizeof(ColorGradingSettings) == 228, "Please update Settings.cpp");
return enabled == rhs.enabled &&
quality == rhs.quality &&
toneMapping == rhs.toneMapping &&
diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp
index a1eba1c066..f3feb2ad30 100644
--- a/libs/viewer/src/SimpleViewer.cpp
+++ b/libs/viewer/src/SimpleViewer.cpp
@@ -137,7 +137,6 @@ static void computeToneMapPlot(ColorGradingSettings& settings, float* plot) {
case ToneMapping::GENERIC:
mapper = new GenericToneMapper(
settings.genericToneMapper.contrast,
- settings.genericToneMapper.shoulder,
settings.genericToneMapper.midGrayIn,
settings.genericToneMapper.midGrayOut,
settings.genericToneMapper.hdrMax
@@ -197,7 +196,6 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo
if (ImGui::CollapsingHeader("Tonemap parameters")) {
GenericToneMapperSettings& generic = colorGrading.genericToneMapper;
ImGui::SliderFloat("Contrast##genericToneMapper", &generic.contrast, 1e-5f, 3.0f);
- ImGui::SliderFloat("Shoulder##genericToneMapper", &generic.shoulder, 0.0f, 1.0f);
ImGui::SliderFloat("Mid-gray in##genericToneMapper", &generic.midGrayIn, 0.0f, 1.0f);
ImGui::SliderFloat("Mid-gray out##genericToneMapper", &generic.midGrayOut, 0.0f, 1.0f);
ImGui::SliderFloat("HDR max", &generic.hdrMax, 1.0f, 64.0f);
diff --git a/libs/viewer/tests/test_settings.cpp b/libs/viewer/tests/test_settings.cpp
index 9ad89d27b2..fda3ff27e7 100644
--- a/libs/viewer/tests/test_settings.cpp
+++ b/libs/viewer/tests/test_settings.cpp
@@ -52,7 +52,6 @@ static const char* JSON_TEST_DEFAULTS = R"TXT(
"toneMapping": "ACES_LEGACY",
"genericToneMapper": {
"contrast": 1.0,
- "shoulder": 1.0,
"midGrayIn": 1.0,
"midGrayOut": 1.0,
"hdrMax": 16.0
From 6805940bbfb13377e44f277910927af8c6a48e7b Mon Sep 17 00:00:00 2001
From: daemyung jang
Date: Thu, 13 Jan 2022 04:06:47 +0900
Subject: [PATCH 08/22] Compute morphing on GPU (#4999)
---
.../filament/filamat/MaterialBuilder.java | 16 +-
.../src/main/cpp/RenderableManager.cpp | 4 +-
.../android/filament/RenderableManager.java | 8 +-
.../google/android/filament/VertexBuffer.java | 16 +-
docs/webgl/reference.html | 8 -
filament/CMakeLists.txt | 3 +
filament/include/filament/Engine.h | 2 +
filament/include/filament/MorphTargetBuffer.h | 119 +++++++++
filament/include/filament/RenderableManager.h | 17 +-
filament/src/Allocators.h | 4 +-
filament/src/Engine.cpp | 13 +
filament/src/Material.cpp | 4 +
filament/src/MorphTargetBuffer.cpp | 225 ++++++++++++++++++
filament/src/RenderPass.cpp | 15 ++
filament/src/RenderPass.h | 13 +-
filament/src/RenderPrimitive.cpp | 4 +
filament/src/RenderPrimitive.h | 5 +
filament/src/Renderer.cpp | 3 +
filament/src/Scene.cpp | 6 +-
filament/src/ShadowMapManager.cpp | 2 +
filament/src/View.cpp | 14 ++
filament/src/components/RenderableManager.cpp | 70 +++++-
filament/src/components/RenderableManager.h | 39 ++-
filament/src/details/Engine.h | 4 +
filament/src/details/MorphTargetBuffer.h | 69 ++++++
filament/src/details/Scene.h | 4 +-
filament/src/details/View.h | 4 +
.../include/filament/MaterialEnums.h | 14 +-
.../include/private/filament/EngineEnums.h | 22 +-
.../include/private/filament/SibGenerator.h | 10 +
.../include/private/filament/UibStructs.h | 10 +-
libs/filabridge/src/SibGenerator.cpp | 15 ++
libs/filamat/src/UibGenerator.cpp | 10 +-
libs/filamat/src/UibGenerator.h | 1 +
libs/filamat/src/shaders/ShaderGenerator.cpp | 14 +-
libs/gltfio/include/gltfio/FilamentAsset.h | 10 +
libs/gltfio/src/Animator.cpp | 6 +-
libs/gltfio/src/AssetLoader.cpp | 36 +--
libs/gltfio/src/FFilamentAsset.h | 9 +-
libs/gltfio/src/FilamentAsset.cpp | 23 ++
libs/gltfio/src/MorphHelper.cpp | 142 ++++-------
libs/gltfio/src/MorphHelper.h | 27 +--
libs/gltfio/src/ResourceLoader.cpp | 15 +-
libs/viewer/include/viewer/SimpleViewer.h | 3 +
libs/viewer/src/SimpleViewer.cpp | 22 ++
shaders/src/getters.vs | 43 +++-
shaders/src/main.vs | 10 +-
web/filament-js/filament.d.ts | 8 -
web/filament-js/jsbindings.cpp | 7 +-
web/filament-js/jsenums.cpp | 10 +-
50 files changed, 853 insertions(+), 305 deletions(-)
create mode 100644 filament/include/filament/MorphTargetBuffer.h
create mode 100644 filament/src/MorphTargetBuffer.cpp
create mode 100644 filament/src/details/MorphTargetBuffer.h
diff --git a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java
index 5017f18fc5..c52cd51e0c 100644
--- a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java
+++ b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java
@@ -114,14 +114,14 @@ public class MaterialBuilder {
BONE_INDICES, // indices of 4 bones (uvec4)
BONE_WEIGHTS, // weights of the 4 bones (normalized float4)
UNUSED, // reserved for future use
- CUSTOM0, // custom or MORPH_POSITION_0
- CUSTOM1, // custom or MORPH_POSITION_1
- CUSTOM2, // custom or MORPH_POSITION_2
- CUSTOM3, // custom or MORPH_POSITION_3
- CUSTOM4, // custom or MORPH_TANGENTS_0
- CUSTOM5, // custom or MORPH_TANGENTS_1
- CUSTOM6, // custom or MORPH_TANGENTS_2
- CUSTOM7 // custom or MORPH_TANGENTS_3
+ CUSTOM0, // custom
+ CUSTOM1, // custom
+ CUSTOM2, // custom
+ CUSTOM3, // custom
+ CUSTOM4, // custom
+ CUSTOM5, // custom
+ CUSTOM6, // custom
+ CUSTOM7 // custom
}
public enum BlendingMode {
diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp
index cba710637c..8d3d43dcdd 100644
--- a/android/filament-android/src/main/cpp/RenderableManager.cpp
+++ b/android/filament-android/src/main/cpp/RenderableManager.cpp
@@ -270,9 +270,9 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
jlong nativeRenderableManager, jint instance, jfloatArray weights) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
jfloat* vec = env->GetFloatArrayElements(weights, NULL);
- math::float4 floatvec(vec[0], vec[1], vec[2], vec[3]);
+ jsize count = env->GetArrayLength(weights);
+ rm->setMorphWeights((RenderableManager::Instance)instance, vec, count);
env->ReleaseFloatArrayElements(weights, vec, JNI_ABORT);
- rm->setMorphWeights((RenderableManager::Instance)instance, floatvec);
}
extern "C" JNIEXPORT void JNICALL
diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
index db4fdb8c83..1a5785ebe7 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
@@ -402,9 +402,6 @@ public class RenderableManager {
/**
* Controls if the renderable has vertex morphing targets, false by default.
*
- * This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer
- * must provide data in the appropriate VertexAttribute slots (MORPH_POSITION_0 etc).
- *
* See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis
* to advance the animation.
*/
@@ -497,14 +494,11 @@ public class RenderableManager {
/**
* Updates the vertex morphing weights on a renderable, all zeroes by default.
*
- * This is specified using a 4-tuple, one float per morph target. If the renderable has fewer
- * than 4 morph targets, then clients should fill the unused components with zeroes.
- *
* The renderable must be built with morphing enabled.
*
* @see Builder#morphing
*/
- public void setMorphWeights(@EntityInstance int i, @NonNull @Size(min = 4) float[] weights) {
+ public void setMorphWeights(@EntityInstance int i, @NonNull float[] weights) {
nSetMorphWeights(mNativeObject, i, weights);
}
diff --git a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java
index d3494e81ef..359505d369 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java
@@ -66,14 +66,14 @@ public class VertexBuffer {
BONE_INDICES, // indices of 4 bones (uvec4)
BONE_WEIGHTS, // weights of the 4 bones (normalized float4)
UNUSED, // reserved for future use
- CUSTOM0, // custom or MORPH_POSITION_0
- CUSTOM1, // custom or MORPH_POSITION_1
- CUSTOM2, // custom or MORPH_POSITION_2
- CUSTOM3, // custom or MORPH_POSITION_3
- CUSTOM4, // custom or MORPH_TANGENTS_0
- CUSTOM5, // custom or MORPH_TANGENTS_1
- CUSTOM6, // custom or MORPH_TANGENTS_2
- CUSTOM7 // custom or MORPH_TANGENTS_3
+ CUSTOM0, // custom
+ CUSTOM1, // custom
+ CUSTOM2, // custom
+ CUSTOM3, // custom
+ CUSTOM4, // custom
+ CUSTOM5, // custom
+ CUSTOM6, // custom
+ CUSTOM7 // custom
}
public enum AttributeType {
diff --git a/docs/webgl/reference.html b/docs/webgl/reference.html
index 3fd55c7de6..6b7510906d 100644
--- a/docs/webgl/reference.html
+++ b/docs/webgl/reference.html
@@ -1438,14 +1438,6 @@ This defines the following functions:
CUSTOM5
CUSTOM6
CUSTOM7
-MORPH_POSITION_0
-MORPH_POSITION_1
-MORPH_POSITION_2
-MORPH_POSITION_3
-MORPH_TANGENTS_0
-MORPH_TANGENTS_1
-MORPH_TANGENTS_2
-MORPH_TANGENTS_3
diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt
index 4049d39130..c9c996a9d3 100644
--- a/filament/CMakeLists.txt
+++ b/filament/CMakeLists.txt
@@ -28,6 +28,7 @@ set(PUBLIC_HDRS
include/filament/LightManager.h
include/filament/Material.h
include/filament/MaterialInstance.h
+ include/filament/MorphTargetBuffer.h
include/filament/Options.h
include/filament/RenderTarget.h
include/filament/RenderableManager.h
@@ -69,6 +70,7 @@ set(SRCS
src/Material.cpp
src/MaterialInstance.cpp
src/MaterialParser.cpp
+ src/MorphTargetBuffer.cpp
src/PerViewUniforms.cpp
src/PostProcessManager.cpp
src/RenderPass.cpp
@@ -140,6 +142,7 @@ set(PRIVATE_HDRS
src/details/IndirectLight.h
src/details/Material.h
src/details/MaterialInstance.h
+ src/details/MorphTargetBuffer.h
src/details/RenderTarget.h
src/details/Renderer.h
src/details/Scene.h
diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h
index 4db5ae7576..0a31013f66 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -39,6 +39,7 @@ class SkinningBuffer;
class IndirectLight;
class Material;
class MaterialInstance;
+class MorphTargetBuffer;
class Renderer;
class RenderTarget;
class Scene;
@@ -421,6 +422,7 @@ public:
bool destroy(const Fence* p); //!< Destroys a Fence object.
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object.
+ bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object.
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
/**
diff --git a/filament/include/filament/MorphTargetBuffer.h b/filament/include/filament/MorphTargetBuffer.h
new file mode 100644
index 0000000000..f3c0877039
--- /dev/null
+++ b/filament/include/filament/MorphTargetBuffer.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TNT_FILAMENT_MORPHTARGETBUFFER_H
+#define TNT_FILAMENT_MORPHTARGETBUFFER_H
+
+#include
+
+#include
+
+#include