Compare commits

..

16 Commits

Author SHA1 Message Date
Benjamin Doherty
60d3638f15 Merge branch 'rc/1.16.0' into release 2022-01-10 10:44:36 -08:00
Benjamin Doherty
3e7d3c9035 Update RELEASE_NOTES for 1.16.0 2022-01-10 10:41:14 -08:00
jeanlemotan
eb360be2ad Fixed cubemap update 2022-01-10 10:34:49 -08:00
Benjamin Doherty
485ac8704d Bump version to 1.16.0 2022-01-04 11:55:19 -08:00
Benjamin Doherty
eaefaf8e59 Release Filament 1.15.2 2022-01-04 11:52:31 -08:00
Ben Doherty
7873e6f5aa Metal: clamp readPixels width and height to texture dimensions (#4994) 2021-12-28 10:27:11 -07:00
abwood
251abf0545 Add support for the glTF extension KHR_materials_emissive_strength (#4975) 2021-12-23 01:32:45 -08:00
Mathias Agopian
b5c4b30ff6 Fix a double release with Android bitmaps callbacks
The version of Texture::setBitmap() that tool an Android bitmap handled
callbacks differently and in certain case caused a "double release" of
the callback object. This didn't actually cause problems though.

We now use the same mechanism used elsewhere (i.e. JniCallback).
2021-12-15 09:18:23 -08:00
Mathias Agopian
eb941a0868 Make normal skinning more efficient
With this change the cofactor matrix is partially precomputed on the 
CPU side.
2021-12-14 10:00:10 -08:00
Mathias Agopian
304ffe0049 Fix skinning calculations
it was incorrect to store bone matrices as quaternions because it is
allowed for these matrices to have a skew component.

We now store the 4x4 matrix -- which is the same amount of data as
before, however we compute the cofactor matrix in the vertex shader when
transforming normals.

fixes #4887
2021-12-14 10:00:10 -08:00
Mathias Agopian
88edbc7df8 Fix a java global reference leak
We were acquiring a reference twice by accident.

Fix #4957
2021-12-13 15:16:48 -08:00
Mathias Agopian
6fa82ea73d Fix typo when setting cubemap faces mip levels. 2021-12-13 15:16:33 -08:00
Mathias Agopian
92e7ff3de7 fix debug checks for compressed textures
Fixes #4941
2021-12-13 15:16:33 -08:00
Romain Guy
2739eb146a Fix rounding math 2021-12-13 10:54:31 -08:00
Romain Guy
59ccc3021f Add missing JNI impl (#4959) 2021-12-13 10:52:47 -08:00
Romain Guy
36e9fa42f4 Fix preprocessor test 2021-12-13 10:42:07 -08:00
27 changed files with 433 additions and 266 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.15.2'
implementation 'com.google.android.filament:filament-android:1.16.0'
}
```
@@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.15.2'
pod 'Filament', '~> 1.16.0'
```
### Snapshots

View File

@@ -3,7 +3,15 @@
This file contains one line summaries of commits that are worthy of mentioning in release notes.
A new header is inserted each time a *tag* is created.
## v1.15.3 (currently main branch)
## v1.16.1 (currently main branch)
## v1.16.0
- engine: Fixes skinning calculations (#4887) [⚠️ **Material breakage**].
- engine: Add support for the glTF extension KHR_materials_emissive_strength.
- engine: Improvements and fixes to skinning calculations.
- engine: Fix debug checks for compressed textures.
- Metal: Fix `readPixels` when dimensions are greater than the render target's.
## v1.15.2

View File

@@ -86,7 +86,6 @@ JniBufferCallback::JniBufferCallback(JNIEnv* env, jobject handler, jobject callb
AutoBuffer&& buffer)
: JniCallback(env, handler, callback),
mBuffer(std::move(buffer)) {
acquireCallbackJni(env, mCallbackUtils);
}
JniBufferCallback::~JniBufferCallback() = default;
@@ -109,7 +108,6 @@ JniImageCallback* JniImageCallback::make(filament::Engine*,
JniImageCallback::JniImageCallback(JNIEnv* env, jobject handler, jobject callback, long image)
: JniCallback(env, handler, callback),
mImage(image) {
acquireCallbackJni(env, mCallbackUtils);
}
JniImageCallback::~JniImageCallback() = default;

View File

@@ -39,30 +39,39 @@ void releaseCallbackJni(JNIEnv* env, CallbackJni callbackUtils, jobject handler,
struct JniCallback : private filament::backend::CallbackHandler {
JniCallback(JniCallback const &) = delete;
JniCallback(JniCallback&&) = delete;
JniCallback& operator=(JniCallback const &) = delete;
JniCallback& operator=(JniCallback&&) = delete;
// create a JniCallback
static JniCallback* make(JNIEnv* env, jobject handler, jobject runnable);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(JniCallback* callback);
// CallbackHandler interface.
void post(void* user, Callback callback) override;
// Get the CallbackHandler interface
filament::backend::CallbackHandler* getHandler() noexcept { return this; }
jobject getCallbackObject() { return mCallback; }
protected:
JniCallback(JNIEnv* env, jobject handler, jobject runnable);
explicit JniCallback() = default; // this version does nothing
virtual ~JniCallback();
jobject mHandler;
jobject mCallback;
CallbackJni mCallbackUtils;
jobject mHandler{};
jobject mCallback{};
CallbackJni mCallbackUtils{};
};
struct JniBufferCallback : public JniCallback {
// create a JniBufferCallback
static JniBufferCallback* make(filament::Engine* engine,
JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(void*, size_t, void* user);
private:
@@ -72,9 +81,11 @@ private:
};
struct JniImageCallback : public JniCallback {
// create a JniImageCallback
static JniImageCallback* make(filament::Engine* engine, JNIEnv* env, jobject handler,
jobject runnable, long image);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(void*, void* user);
private:

View File

@@ -31,6 +31,8 @@
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include "private/backend/VirtualMachineEnv.h"
using namespace filament;
using namespace backend;
@@ -480,46 +482,37 @@ Java_com_google_android_filament_Texture_nGeneratePrefilterMipmap(JNIEnv *env, j
#define BITMAP_CONFIG_RGBA_F16 4
#define BITMAP_CONFIG_HARDWARE 5
class AutoBitmap {
public:
class AutoBitmap : public JniCallback {
private:
AutoBitmap(JNIEnv* env, jobject bitmap) noexcept
: mEnv(env)
, mBitmap(env->NewGlobalRef(bitmap))
{
: JniCallback(),
mBitmap(env->NewGlobalRef(bitmap)) {
if (mBitmap) {
AndroidBitmap_getInfo(mEnv, mBitmap, &mInfo);
AndroidBitmap_lockPixels(mEnv, mBitmap, &mData);
AndroidBitmap_getInfo(env, mBitmap, &mInfo);
AndroidBitmap_lockPixels(env, mBitmap, &mData);
}
}
AutoBitmap(JNIEnv* env, jobject bitmap, jobject handler, jobject runnable) noexcept
: mEnv(env)
, mBitmap(env->NewGlobalRef(bitmap))
, mHandler(env->NewGlobalRef(handler))
, mCallback(env->NewGlobalRef(runnable))
{
acquireCallbackJni(env, mCallbackUtils);
: JniCallback(env, handler, runnable),
mBitmap(env->NewGlobalRef(bitmap)) {
if (mBitmap) {
AndroidBitmap_getInfo(mEnv, mBitmap, &mInfo);
AndroidBitmap_lockPixels(mEnv, mBitmap, &mData);
AndroidBitmap_getInfo(env, mBitmap, &mInfo);
AndroidBitmap_lockPixels(env, mBitmap, &mData);
}
}
~AutoBitmap() noexcept {
releaseCallbackJni(mEnv, mCallbackUtils, mHandler, mCallback);
void release(JNIEnv* env) {
if (mBitmap) {
AndroidBitmap_unlockPixels(mEnv, mBitmap);
mEnv->DeleteGlobalRef(mBitmap);
AndroidBitmap_unlockPixels(env, mBitmap);
env->DeleteGlobalRef(mBitmap);
}
}
AutoBitmap(AutoBitmap &&rhs) noexcept {
mEnv = rhs.mEnv;
std::swap(mData, rhs.mData);
std::swap(mBitmap, rhs.mBitmap);
std::swap(mInfo, rhs.mInfo);
}
~AutoBitmap() override = default;
public:
void* getData() const noexcept {
return mData;
}
@@ -546,28 +539,39 @@ public:
}
}
static void invoke(void* buffer, size_t n, void* user) {
AutoBitmap* data = reinterpret_cast<AutoBitmap*>(user);
delete data;
}
static AutoBitmap* make(Engine* engine, JNIEnv* env, jobject bitmap) {
return new AutoBitmap(env, bitmap);
}
static AutoBitmap* make(Engine* engine, JNIEnv* env, jobject bitmap,
jobject handler, jobject runnable) {
// create a AutoBitmap
static AutoBitmap* make(JNIEnv* env, jobject bitmap, jobject handler, jobject runnable) {
return new AutoBitmap(env, bitmap, handler, runnable);
}
// execute the callback on the java thread and destroy ourselves
static void invoke(void*, size_t, void* user) {
auto* autoBitmap = reinterpret_cast<AutoBitmap*>(user);
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
releaseCallbackJni(env, autoBitmap->mCallbackUtils, autoBitmap->mHandler, autoBitmap->mCallback);
autoBitmap->release(env);
delete autoBitmap;
}
// create a AutoBitmap without a handler
static AutoBitmap* make(JNIEnv* env, jobject bitmap) {
return new AutoBitmap(env, bitmap);
}
// just destroy ourselves
static void invokeNoCallback(void*, size_t, void* user) {
auto* autoBitmap = reinterpret_cast<AutoBitmap*>(user);
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
autoBitmap->release(env);
delete autoBitmap;
}
private:
JNIEnv* mEnv;
void* mData = nullptr;
jobject mBitmap = nullptr;
jobject mHandler = nullptr;
jobject mCallback = nullptr;
AndroidBitmapInfo mInfo{};
CallbackJni mCallbackUtils;
};
extern "C"
@@ -578,14 +582,14 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmap(JNIEnv* env, j
Texture* texture = (Texture*) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
auto* autoBitmap = AutoBitmap::make(engine, env, bitmap);
auto* autoBitmap = AutoBitmap::make(env, bitmap);
Texture::PixelBufferDescriptor desc(
autoBitmap->getData(),
autoBitmap->getSizeInBytes(),
autoBitmap->getFormat(format),
autoBitmap->getType(format),
&AutoBitmap::invoke, autoBitmap);
&AutoBitmap::invokeNoCallback, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
@@ -601,14 +605,14 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JN
Texture* texture = (Texture*) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
auto* autoBitmap = AutoBitmap::make(engine, env, bitmap, handler, runnable);
auto* autoBitmap = AutoBitmap::make(env, bitmap, handler, runnable);
Texture::PixelBufferDescriptor desc(
autoBitmap->getData(),
autoBitmap->getSizeInBytes(),
autoBitmap->getFormat(format),
autoBitmap->getType(format),
&AutoBitmap::invoke, autoBitmap);
autoBitmap->getHandler(), &AutoBitmap::invoke, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.15.2
VERSION_NAME=1.16.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -979,6 +979,11 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
id<MTLTexture> srcTexture = color.getTexture();
size_t miplevel = color.level;
// Clamp height and width to actual texture's height and width
MTLSize srcTextureSize = MTLSizeMake(srcTexture.width >> miplevel, srcTexture.height >> miplevel, 1);
height = std::min(static_cast<uint32_t>(srcTextureSize.height), height);
width = std::min(static_cast<uint32_t>(srcTextureSize.width), width);
const MTLPixelFormat format = getMetalFormat(data.format, data.type);
ASSERT_PRECONDITION(format != MTLPixelFormatInvalid,
"The chosen combination of PixelDataFormat (%d) and PixelDataType (%d) is not supported for "
@@ -994,8 +999,8 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
MTLTextureDescriptor* textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:(srcTexture.width >> miplevel)
height:(srcTexture.height >> miplevel)
width:srcTextureSize.width
height:srcTextureSize.height
mipmapped:NO];
#if defined(IOS)
textureDescriptor.storageMode = MTLStorageModeShared;

View File

@@ -1807,10 +1807,12 @@ void OpenGLDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture *>(th);
auto width = std::max(1u, t->width >> level);
auto height = std::max(1u, t->height >> level);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t, level, 0, 0, 0, 0, 0, 0, std::move(data), &faceOffsets);
setCompressedTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
} else {
setTextureData(t, level, 0, 0, 0, 0, 0, 0, std::move(data), &faceOffsets);
setTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
}
}
@@ -1907,7 +1909,7 @@ void OpenGLDriver::setTextureData(GLTexture* t,
for (size_t face = 0; face < 6; face++) {
GLenum target = getCubemapTarget(TextureCubemapFace(face));
glTexSubImage2D(target, GLint(level), 0, 0,
t->width >> level, t->height >> level, glFormat, glType,
width, height, glFormat, glType,
static_cast<uint8_t const*>(p.buffer) + offsets[face]);
}
break;
@@ -1938,8 +1940,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
DEBUG_MARKER()
auto& gl = mContext;
assert_invariant(xoffset + width <= t->width >> level);
assert_invariant(yoffset + height <= t->height >> level);
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
assert_invariant(yoffset + height <= std::max(1u, t->height >> level));
assert_invariant(zoffset + depth <= t->depth);
assert_invariant(t->samples <= 1);
@@ -1992,7 +1994,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
for (size_t face = 0; face < 6; face++) {
GLenum target = getCubemapTarget(TextureCubemapFace(face));
glCompressedTexSubImage2D(target, GLint(level), 0, 0,
t->width >> level, t->height >> level, t->gl.internalFormat,
width, height, t->gl.internalFormat,
imageSize, static_cast<uint8_t const*>(p.buffer) + offsets[face]);
}
break;

View File

@@ -22,10 +22,9 @@
#include "FilamentAPI-impl.h"
#include <math/half.h>
#include <math/mat4.h>
#include <float.h>
namespace filament {
using namespace backend;
@@ -111,55 +110,61 @@ void FSkinningBuffer::setBones(FEngine& engine,
setBones(engine, mHandle, transforms, count, offset);
}
static uint32_t packHalf2x16(half2 v) noexcept {
uint32_t lo = getBits(v[0]);
uint32_t hi = getBits(v[1]);
return (hi << 16) | lo;
}
void FSkinningBuffer::setBones(FEngine& engine, Handle<backend::HwBufferObject> handle,
RenderableManager::Bone const* transforms, size_t boneCount, size_t offset) noexcept {
auto& driverApi = engine.getDriverApi();
size_t size = boneCount * sizeof(PerRenderableUibBone);
PerRenderableUibBone* UTILS_RESTRICT out = (PerRenderableUibBone*)driverApi.allocate(size);
for (size_t i = 0, c = boneCount; i < c; ++i) {
out[i].q = transforms[i].unitQuaternion;
out[i].t.xyz = transforms[i].translation;
out[i].s = out[i].ns = { 1, 1, 1, 0 };
// the transform is stored in row-major, last row is not stored.
mat4f transform(transforms[i].unitQuaternion);
transform[3] = float4{ transforms[i].translation, 1.0f };
out[i] = makeBone(transform);
}
driverApi.updateBufferObject(handle, { out, size },
offset * sizeof(PerRenderableUibBone));
}
PerRenderableUibBone FSkinningBuffer::makeBone(mat4f transform) noexcept {
const mat3f cofactors = cof(transform.upperLeft());
transform = transpose(transform); // row-major conversion
return {
.bone = {
.transform = {
transform[0],
transform[1],
transform[2]
},
.cof = {
packHalf2x16({ cofactors[0].x, cofactors[0].y }),
packHalf2x16({ cofactors[0].z, cofactors[1].x }),
packHalf2x16({ cofactors[1].y, cofactors[1].z }),
packHalf2x16({ cofactors[2].x, cofactors[2].y })
// cofactor[2][2] is not stored because we don't have space for it
}
}
};
}
void FSkinningBuffer::setBones(FEngine& engine, Handle<backend::HwBufferObject> handle,
mat4f const* transforms, size_t boneCount, size_t offset) noexcept {
auto& driverApi = engine.getDriverApi();
size_t size = boneCount * sizeof(PerRenderableUibBone);
PerRenderableUibBone* UTILS_RESTRICT out = (PerRenderableUibBone*)driverApi.allocate(size);
for (size_t i = 0, c = boneCount; i < c; ++i) {
FSkinningBuffer::makeBone(&out[i], transforms[i]);
// the transform is stored in row-major, last row is not stored.
out[i] = makeBone(transforms[i]);
}
driverApi.updateBufferObject(handle, { out, size },
offset * sizeof(PerRenderableUibBone));
}
void FSkinningBuffer::makeBone(PerRenderableUibBone* UTILS_RESTRICT out, mat4f const& t) noexcept {
mat4f m(t);
// figure out the scales
float4 s = { length(m[0]), length(m[1]), length(m[2]), 0.0f };
s = max(s, float4(FLT_EPSILON)); // avoid divide-by-zero when computing inverse scales
if (dot(cross(m[0].xyz, m[1].xyz), m[2].xyz) < 0) {
s[2] = -s[2];
}
// compute the inverse scales
float4 is = { 1.0f/s.x, 1.0f/s.y, 1.0f/s.z, 0.0f };
// normalize the matrix
m[0] *= is[0];
m[1] *= is[1];
m[2] *= is[2];
out->s = s;
out->q = m.toQuaternion();
out->t = m[3];
out->ns = is / max(abs(is));
}
// ------------------------------------------------------------------------------------------------
// Trampoline calling into private implementation

View File

@@ -57,14 +57,14 @@ private:
friend class SkinningBuffer;
friend class FRenderableManager;
static void makeBone(PerRenderableUibBone* out, math::mat4f const& transforms) noexcept;
static void setBones(FEngine& engine, backend::Handle<backend::HwBufferObject> handle,
RenderableManager::Bone const* transforms, size_t boneCount, size_t offset) noexcept;
static void setBones(FEngine& engine, backend::Handle<backend::HwBufferObject> handle,
math::mat4f const* transforms, size_t boneCount, size_t offset) noexcept;
static PerRenderableUibBone makeBone(math::mat4f transform) noexcept;
backend::Handle<backend::HwBufferObject> getHwHandle() const noexcept {
return mHandle;
}

View File

@@ -727,139 +727,6 @@ TEST(FilamentTest, FroxelData) {
Engine::destroy((Engine **)&engine);
}
TEST(FilamentTest, Bones) {
struct Shader {
static mat3f normal(PerRenderableUibBone const& bone) noexcept {
quatf q = bone.q;
float3 is = bone.ns.xyz;
return mat3f(mat3(q) * mat3::scaling(is));
}
static mat4f vertice(PerRenderableUibBone const& bone) noexcept {
quatf q = bone.q;
float3 t = bone.t.xyz;
float3 s = bone.s.xyz;
return mat4f(mat4::translation(t) * mat4(q) * mat4::scaling(s));
}
static float3 normal(float3 n, PerRenderableUibBone const& bone) noexcept {
quatf q = bone.q;
float3 is = bone.ns.xyz;
// apply the inverse of the non-uniform scales
n *= is;
// apply the rigid transform
n += 2.0 * cross(q.xyz, cross(q.xyz, n) + q.w * n);
return n;
}
static float3 vertice(float3 v, PerRenderableUibBone const& bone) noexcept {
quatf q = bone.q;
float3 t = bone.t.xyz;
float3 s = bone.s.xyz;
// apply the non-uniform scales
v *= s;
// apply the rigid transform
v += 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
// apply the translation
v += t;
return v;
}
};
struct Test {
static inline double epsilon(double x, double y) {
double maxXYOne = std::max({ 1.0, std::fabs(x), std::fabs(y) });
return 1e-5 * maxXYOne;
}
static void expect_eq(mat4f e, mat4f a) noexcept {
for (size_t j = 0; j < 4; j++) {
for (size_t i = 0; i < 4; i++) {
EXPECT_NEAR(e[i][j], a[i][j], epsilon(e[i][j], a[i][j]));
}
}
}
static void expect_eq(mat3f e, mat3f a) noexcept {
for (size_t j = 0; j < 3; j++) {
for (size_t i = 0; i < 3; i++) {
EXPECT_NEAR(e[i][j], a[i][j], epsilon(e[i][j], a[i][j]));
}
}
}
static void expect_eq(float3 e, float3 a) noexcept {
for (size_t i = 0; i < 3; i++) {
EXPECT_NEAR(e[i], a[i], epsilon(e[i], a[i]));
}
}
static void check(mat4f const& m) noexcept {
PerRenderableUibBone b;
FSkinningBuffer::makeBone(&b, m);
expect_eq(Shader::vertice(b), m);
mat3f n = transpose(inverse(m.upperLeft()));
n *= mat3f(1.0f / std::sqrt(max(float3{length2(n[0]), length2(n[1]), length2(n[2])})));
expect_eq(Shader::normal(b), n);
}
static void check(mat4f const& m, float3 const& v) noexcept {
PerRenderableUibBone b;
FSkinningBuffer::makeBone(&b, m);
expect_eq((m * v).xyz, Shader::vertice(v, b));
mat3f n = transpose(inverse(m.upperLeft()));
n *= mat3f(1.0f / std::sqrt(max(float3{length2(n[0]), length2(n[1]), length2(n[2])})));
expect_eq(n * normalize(v), Shader::normal(normalize(v), b));
float3 normal = n * normalize(v);
EXPECT_LE(max(abs(normal)), 1.0);
}
};
Test::check(mat4f{});
Test::check(mat4f::translation(float3{ 1, 2, 3 }));
Test::check(mat4f::scaling(float3{ 2, 2, 2 }));
Test::check(mat4f::scaling(float3{ 4, 2, 3 }));
Test::check(mat4f::scaling(float3{ 4, -2, -3 }));
Test::check(mat4f::scaling(float3{ -4, 2, -3 }));
Test::check(mat4f::scaling(float3{ -4, -2, 3 }));
Test::check(mat4f::scaling(float3{ -4, -2, -3 }));
Test::check(mat4f::scaling(float3{ -4, 2, 3 }));
Test::check(mat4f::scaling(float3{ 4, -2, 3 }));
Test::check(mat4f::scaling(float3{ 4, 2, -3 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 0, 0, 1 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 0, 1, 0 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 1, 0, 0 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 0, 1, 1 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 1, 0, 1 }));
Test::check(mat4f::rotation(F_PI_2, float3{ 1, 1, 0 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 0, 0, 1 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 0, 1, 0 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 1, 0, 0 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 0, 1, 1 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 1, 0, 1 }));
Test::check(mat4f::rotation(-F_PI_2, float3{ 1, 1, 0 }));
mat4f m = mat4f::translation(float3{ 1, 2, 3 }) *
mat4f::rotation(-F_PI_2, float3{ 1, 1, 0 }) *
mat4f::scaling(float3{ -2, 3, 0.04 });
Test::check(m);
std::default_random_engine generator(82828);
std::uniform_real_distribution<float> distribution(-100.0f, 100.0f);
auto rand_gen = std::bind(distribution, generator);
for (size_t i = 0; i < 100; ++i) {
float3 p(rand_gen(), rand_gen(), rand_gen());
Test::check(m, p);
}
}
TEST(FilamentTest, GoogleLineDirective) {
{
char s[512] = "#line 10 \"foobar\"";

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.15.2"
spec.version = "1.16.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.15.2/filament-v1.15.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.16.0/filament-v1.16.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 15;
static constexpr size_t MATERIAL_VERSION = 16;
/**
* Supported shading models

View File

@@ -212,10 +212,17 @@ static_assert(sizeof(FroxelRecordUib) == 16384, "FroxelRecordUib should be exact
// This is not the UBO proper, but just an element of a bone array.
struct PerRenderableUibBone { // NOLINT(cppcoreguidelines-pro-type-member-init)
static constexpr utils::StaticString _name{ "BonesUniforms" };
math::quatf q = { 1, 0, 0, 0 };
math::float4 t = {};
math::float4 s = { 1, 1, 1, 0 };
math::float4 ns = { 1, 1, 1, 0 };
struct alignas(16) BoneData {
// bone transform, last row assumed [0,0,0,1]
math::float4 transform[3] = {
{1,0,0,0},
{0,1,0,0},
{0,0,1,0}
};
// 8 first cofactor matrix of transform's upper left
math::uint4 cof = {};
};
BoneData bone;
};
static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384,
"PerRenderableUibBone exceed max UBO size");

View File

@@ -156,7 +156,7 @@ UniformInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
UniformInterfaceBlock const& UibGenerator::getPerRenderableBonesUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
.name(PerRenderableUibBone::_name)
.add("bones", CONFIG_MAX_BONE_COUNT * 4, UniformInterfaceBlock::Type::FLOAT4, Precision::MEDIUM)
.add("bones", CONFIG_MAX_BONE_COUNT, "BoneData", sizeof(PerRenderableUibBone::BoneData))
.build();
return uib;
}

View File

@@ -1029,8 +1029,11 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp
mi->setMaskThreshold(inputMat->alpha_cutoff);
}
const float* e = inputMat->emissive_factor;
mi->setParameter("emissiveFactor", float3(e[0], e[1], e[2]));
float3 emissiveFactor(inputMat->emissive_factor[0], inputMat->emissive_factor[1], inputMat->emissive_factor[2]);
if (inputMat->has_emissive_strength) {
emissiveFactor *= inputMat->emissive_strength.emissive_strength;
}
mi->setParameter("emissiveFactor", emissiveFactor);
const float* c = mrConfig.base_color_factor;
mi->setParameter("baseColorFactor", float4(c[0], c[1], c[2], c[3]));

View File

@@ -159,3 +159,27 @@ void toTangentFrame(const highp vec4 q, out highp vec3 n, out highp vec3 t) {
vec3(-2.0, 2.0, -2.0) * q.y * q.yxw +
vec3(-2.0, 2.0, 2.0) * q.z * q.zwx;
}
highp mat3 cofactor(const highp mat3 m) {
highp float a = m[0][0];
highp float b = m[1][0];
highp float c = m[2][0];
highp float d = m[0][1];
highp float e = m[1][1];
highp float f = m[2][1];
highp float g = m[0][2];
highp float h = m[1][2];
highp float i = m[2][2];
highp mat3 cof;
cof[0][0] = e * i - f * h;
cof[0][1] = c * h - b * i;
cof[0][2] = b * f - c * e;
cof[1][0] = f * g - d * i;
cof[1][1] = a * i - c * g;
cof[1][2] = c * d - a * f;
cof[2][0] = d * h - e * g;
cof[2][1] = b * g - a * h;
cof[2][2] = a * e - b * d;
return cof;
}

View File

@@ -34,3 +34,8 @@ struct ShadowData {
float bulbRadiusLs;
float nearOverFarMinusNear;
};
struct BoneData {
highp mat3x4 transform; // bone transform is mat4x3 stored in row-major (last row [0,0,0,1])
highp uvec4 cof; // 8 first cofactor matrix of transform's upper left
};

View File

@@ -22,44 +22,46 @@ mat3 getWorldFromModelNormalMatrix() {
#if defined(HAS_SKINNING_OR_MORPHING)
vec3 mulBoneNormal(vec3 n, uint i) {
vec4 q = bonesUniforms.bones[i + 0u];
vec3 is = bonesUniforms.bones[i + 3u].xyz;
// apply the inverse of the non-uniform scales
n *= is;
// apply the rigid transform (valid only for unit quaternions)
n += 2.0 * cross(q.xyz, cross(q.xyz, n) + q.w * n);
highp mat3 cof;
return n;
// the first 8 elements of the cofactor matrix are stored as fp16
highp vec2 zx = unpackHalf2x16(bonesUniforms.bones[i].cof[1]);
cof[0].xy = unpackHalf2x16(bonesUniforms.bones[i].cof[0]);
cof[0].z = zx[0];
cof[1].x = zx[1];
cof[1].yz = unpackHalf2x16(bonesUniforms.bones[i].cof[2]);
cof[2].xy = unpackHalf2x16(bonesUniforms.bones[i].cof[3]);
// the last element must be computed by hand
highp float a = bonesUniforms.bones[i].transform[0][0];
highp float b = bonesUniforms.bones[i].transform[0][1];
highp float d = bonesUniforms.bones[i].transform[1][0];
highp float e = bonesUniforms.bones[i].transform[1][1];
cof[2].z = a * e - b * d;
return normalize(cof * n);
}
vec3 mulBoneVertex(vec3 v, uint i) {
vec4 q = bonesUniforms.bones[i + 0u];
vec3 t = bonesUniforms.bones[i + 1u].xyz;
vec3 s = bonesUniforms.bones[i + 2u].xyz;
// apply the non-uniform scales
v *= s;
// apply the rigid transform (valid only for unit quaternions)
v += 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
// apply the translation
v += t;
return v;
// last row of bonesUniforms.transform[i] (row major) is assumed to be [0,0,0,1]
highp mat4x3 m = transpose(bonesUniforms.bones[i].transform);
return v.x * m[0].xyz + (v.y * m[1].xyz + (v.z * m[2].xyz + m[3].xyz));
}
void skinNormal(inout vec3 n, const uvec4 ids, const vec4 weights) {
n = mulBoneNormal(n, ids.x * 4u) * weights.x
+ mulBoneNormal(n, ids.y * 4u) * weights.y
+ mulBoneNormal(n, ids.z * 4u) * weights.z
+ mulBoneNormal(n, ids.w * 4u) * weights.w;
n = mulBoneNormal(n, ids.x) * weights.x
+ mulBoneNormal(n, ids.y) * weights.y
+ mulBoneNormal(n, ids.z) * weights.z
+ mulBoneNormal(n, ids.w) * weights.w;
}
void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) {
p = mulBoneVertex(p, ids.x * 4u) * weights.x
+ mulBoneVertex(p, ids.y * 4u) * weights.y
+ mulBoneVertex(p, ids.z * 4u) * weights.z
+ mulBoneVertex(p, ids.w * 4u) * weights.w;
p = mulBoneVertex(p, ids.x) * weights.x
+ mulBoneVertex(p, ids.y) * weights.y
+ mulBoneVertex(p, ids.z) * weights.z
+ mulBoneVertex(p, ids.w) * weights.w;
}
#endif

View File

@@ -112,6 +112,8 @@ cgltf also supports some glTF extensions:
- KHR_materials_variants
- KHR_materials_volume
- KHR_texture_transform
- KHR_texture_basisu (requires a library like [Binomial Basisu](https://github.com/BinomialLLC/basis_universal) for transcoding to native compressed texture)
- KHR_materials_emissive_strength
cgltf does **not** yet support unlisted extensions. However, unlisted extensions can be accessed via "extensions" member on objects.

View File

@@ -474,6 +474,11 @@ typedef struct cgltf_sheen
cgltf_float sheen_roughness_factor;
} cgltf_sheen;
typedef struct cgltf_emissive_strength
{
cgltf_float emissive_strength;
} cgltf_emissive_strength;
typedef struct cgltf_material
{
char* name;
@@ -485,6 +490,7 @@ typedef struct cgltf_material
cgltf_bool has_ior;
cgltf_bool has_specular;
cgltf_bool has_sheen;
cgltf_bool has_emissive_strength;
cgltf_pbr_metallic_roughness pbr_metallic_roughness;
cgltf_pbr_specular_glossiness pbr_specular_glossiness;
cgltf_clearcoat clearcoat;
@@ -493,6 +499,7 @@ typedef struct cgltf_material
cgltf_sheen sheen;
cgltf_transmission transmission;
cgltf_volume volume;
cgltf_emissive_strength emissive_strength;
cgltf_texture_view normal_texture;
cgltf_texture_view occlusion_texture;
cgltf_texture_view emissive_texture;
@@ -779,7 +786,8 @@ cgltf_result cgltf_load_buffers(
cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data);
void cgltf_decode_uri(char* uri);
cgltf_size cgltf_decode_string(char* string);
cgltf_size cgltf_decode_uri(char* uri);
cgltf_result cgltf_validate(cgltf_data* data);
@@ -1265,7 +1273,78 @@ static int cgltf_unhex(char ch)
-1;
}
void cgltf_decode_uri(char* uri)
cgltf_size cgltf_decode_string(char* string)
{
char* read = string + strcspn(string, "\\");
if (*read == 0)
{
return read - string;
}
char* write = string;
char* last = string;
for (;;)
{
// Copy characters since last escaped sequence
cgltf_size written = read - last;
memmove(write, last, written);
write += written;
if (*read++ == 0)
{
break;
}
// jsmn already checked that all escape sequences are valid
switch (*read++)
{
case '\"': *write++ = '\"'; break;
case '/': *write++ = '/'; break;
case '\\': *write++ = '\\'; break;
case 'b': *write++ = '\b'; break;
case 'f': *write++ = '\f'; break;
case 'r': *write++ = '\r'; break;
case 'n': *write++ = '\n'; break;
case 't': *write++ = '\t'; break;
case 'u':
{
// UCS-2 codepoint \uXXXX to UTF-8
int character = 0;
for (cgltf_size i = 0; i < 4; ++i)
{
character = (character << 4) + cgltf_unhex(*read++);
}
if (character <= 0x7F)
{
*write++ = character & 0xFF;
}
else if (character <= 0x7FF)
{
*write++ = 0xC0 | ((character >> 6) & 0xFF);
*write++ = 0x80 | (character & 0x3F);
}
else
{
*write++ = 0xE0 | ((character >> 12) & 0xFF);
*write++ = 0x80 | ((character >> 6) & 0x3F);
*write++ = 0x80 | (character & 0x3F);
}
break;
}
default:
break;
}
last = read;
read += strcspn(read, "\\");
}
*write = 0;
return write - string;
}
cgltf_size cgltf_decode_uri(char* uri)
{
char* write = uri;
char* i = uri;
@@ -1293,6 +1372,7 @@ void cgltf_decode_uri(char* uri)
}
*write = 0;
return write - uri;
}
cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path)
@@ -3771,6 +3851,39 @@ static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* token
return i;
}
static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i].size;
++i;
// Default
out_emissive_strength->emissive_strength = 1.f;
for (int j = 0; j < size; ++j)
{
CGLTF_CHECK_KEY(tokens[i]);
if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0)
{
++i;
out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk);
++i;
}
else
{
i = cgltf_skip_json(tokens, i + 1);
}
if (i < 0)
{
return i;
}
}
return i;
}
static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
@@ -4145,6 +4258,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
out_material->has_sheen = 1;
i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0)
{
out_material->has_emissive_strength = 1;
i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength);
}
else
{
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++]));

View File

@@ -59,7 +59,7 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si
#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
/* This makes MSVC/CLion intellisense work. */
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#endif
#ifdef CGLTF_WRITE_IMPLEMENTATION
@@ -84,6 +84,7 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si
#define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS (1 << 10)
#define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME (1 << 11)
#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU (1 << 12)
#define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13)
typedef struct {
char* buffer;
@@ -587,6 +588,11 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN;
}
if (material->has_emissive_strength)
{
context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH;
}
if (material->has_pbr_metallic_roughness)
{
const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness;
@@ -603,7 +609,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
cgltf_write_line(context, "}");
}
if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume)
if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength)
{
cgltf_write_line(context, "\"extensions\": {");
if (material->has_clearcoat)
@@ -695,6 +701,13 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
{
cgltf_write_line(context, "\"KHR_materials_unlit\": {}");
}
if (material->has_emissive_strength)
{
cgltf_write_line(context, "\"KHR_materials_emissive_strength\": {");
const cgltf_emissive_strength* params = &material->emissive_strength;
cgltf_write_floatprop(context, "emissiveStrength", params->emissive_strength, 1.f);
cgltf_write_line(context, "}");
}
cgltf_write_line(context, "}");
}
@@ -1097,6 +1110,9 @@ static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extens
if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) {
cgltf_write_stritem(context, "KHR_texture_basisu");
}
if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH) {
cgltf_write_stritem(context, "KHR_materials_emissive_strength");
}
}
cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data)

View File

@@ -10,6 +10,8 @@ if(MSVC)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
@@ -21,6 +23,8 @@ if(MSVC)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
@@ -32,6 +36,8 @@ if(MSVC)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
@@ -43,5 +49,20 @@ if(MSVC)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_strings )
add_executable( ${EXE_NAME} test_strings.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )

View File

@@ -62,5 +62,11 @@ if __name__ == "__main__":
print("Error.")
sys.exit(1)
result = os.system(get_executable_path("test_strings"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
print("Tested files: " + str(num_tested))
print("Errors: " + str(num_errors))

View File

@@ -26,7 +26,13 @@ int main(int argc, char** argv)
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (result != cgltf_result_success || strstr(argv[1], "Draco"))
if (strstr(argv[1], "Draco"))
{
cgltf_free(data);
return 0;
}
if (result != cgltf_result_success)
return result;
//const cgltf_accessor* blobs = data->accessors;

57
third_party/cgltf/test/test_strings.cpp vendored Normal file
View File

@@ -0,0 +1,57 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <cstring>
static void check(const char* a, const char* b, cgltf_size size) {
if (strcmp(a, b) != 0 || strlen(a) != size) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
char string[64];
cgltf_size size;
// cgltf_decode_string
strcpy(string, "");
size = cgltf_decode_string(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_string(string);
check(string, "nothing to replace", size);
strcpy(string, "\\\" \\/ \\\\ \\b \\f \\r \\n \\t \\u0030");
size = cgltf_decode_string(string);
check(string, "\" / \\ \b \f \r \n \t 0", size);
strcpy(string, "test \\u121b\\u130d\\u1294\\u1276\\u127d test");
size = cgltf_decode_string(string);
check(string, "test ማግኔቶች test", size);
// cgltf_decode_uri
strcpy(string, "");
size = cgltf_decode_uri(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_uri(string);
check(string, "nothing to replace", size);
strcpy(string, "%2F%D0%BA%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0");
size = cgltf_decode_uri(string);
check(string, "/кириллица", size);
strcpy(string, "test%20%E1%88%9B%E1%8C%8D%E1%8A%94%E1%89%B6%E1%89%BD%20test");
size = cgltf_decode_uri(string);
check(string, "test ማግኔቶች test", size);
strcpy(string, "%%2F%X%AX%%2F%%");
size = cgltf_decode_uri(string);
check(string, "%/%X%AX%/%%", size);
return 0;
}

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.15.2",
"version": "1.16.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",