diff --git a/README.md b/README.md index 2683d45723..bddd546eea 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.12.5' + implementation 'com.google.android.filament:filament-android:1.12.6' } ``` @@ -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.12.5' +pod 'Filament', '~> 1.12.6' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0f72e030db..f875cd8391 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,16 @@ A new header is inserted each time a *tag* is created. ## v1.12.6 (currently main branch) +- engine: Added concept of lod bias to materials. + [⚠️ **Materials need to be rebuilt to access this new feature**]. +- engine: Fix, BGRA ordering respected for external images with OpenGL on iOS. +- engine: Use more sensible defaults for spot light inner outer cone angles. +- engine: Fix potential race condition that caused stalls in `endFrame`. +- gltfio: Improved handling of transparent materials. +- Metal: Fix potential crash on earlier versions of iOS (<= 13.0). +- Android: Fix `filament-utils-android` 'lite' flavor. +- Java: Fix potential crash with `IBLPrefilter`. + ## v1.12.5 - engine: work around a job scheduling issue in `endFrame` that caused stuttering on some Android diff --git a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java index e223bb1e24..73ff59b1a7 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java +++ b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java @@ -78,7 +78,7 @@ public class MaterialInstance { public static MaterialInstance duplicate(@NonNull MaterialInstance other, String name) { long nativeInstance = nDuplicate(other.mNativeObject, name); if (nativeInstance == 0) throw new IllegalStateException("Couldn't duplicate MaterialInstance"); - return new MaterialInstance(other.mMaterial, nativeInstance); + return new MaterialInstance(other.getMaterial(), nativeInstance); } /** @return the {@link Material} associated with this instance */ diff --git a/android/filament-utils-android/build.gradle b/android/filament-utils-android/build.gradle index 1bca557d36..ab5614df54 100644 --- a/android/filament-utils-android/build.gradle +++ b/android/filament-utils-android/build.gradle @@ -9,6 +9,13 @@ android { lite { dimension "functionality" + externalNativeBuild { + cmake { + // hack: this is needed because filament-utils-android/CMakeLists.txt builds + // gltfio from source, which needs this defined + arguments.add("-DGLTFIO_LITE=ON") + } + } } } diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/IBLPrefilterContext.java b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/IBLPrefilterContext.java index faa71a5a21..0387d39e98 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/IBLPrefilterContext.java +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/IBLPrefilterContext.java @@ -32,9 +32,13 @@ import com.google.android.filament.Texture; * Texture equirect = HDRLoader.createTexture("foo.hdr"); * Texture skyboxTexture = equirectangularToCubemap.run(equirect); * engine.destroy(equirect); + * equirectangularToCubemap.destroy(); * * specularFilter = new IBLPrefilterContext.SpecularFilter(context); * Texture reflections = specularFilter.run(skyboxTexture); + * specularFilter.destroy(); + * + * context.destroy(); * * IndirectLight ibl = IndirectLight.Builder() * .reflections(reflections) @@ -43,81 +47,74 @@ import com.google.android.filament.Texture; * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public class IBLPrefilterContext { - private final long mNativeObject; + private long mNativeObject; public IBLPrefilterContext(Engine engine) { mNativeObject = nCreate(engine.getNativeObject()); if (mNativeObject == 0) throw new IllegalStateException("Couldn't create IBLPrefilterContext"); } - @Override - protected void finalize() throws Throwable { - nDestroy(mNativeObject); - super.finalize(); + public void destroy() { + nDestroy(getNativeObject()); + mNativeObject = 0; } public static class EquirectangularToCubemap { - @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources - private final HelperFinalizer mFinalizer; - private final long mNativeHelper; + private long mNativeObject; public EquirectangularToCubemap(IBLPrefilterContext context) { - mNativeHelper = nCreateEquirectHelper(context.mNativeObject); - mFinalizer = new HelperFinalizer(mNativeHelper); + mNativeObject = nCreateEquirectHelper(context.getNativeObject()); } public Texture run(Texture equirect) { - long nativeTexture = nEquirectHelperRun(mNativeHelper, equirect.getNativeObject()); + long nativeTexture = nEquirectHelperRun(getNativeObject(), equirect.getNativeObject()); return new Texture(nativeTexture); } - private static class HelperFinalizer { - private final long mNativeObject; + public void destroy() { + nDestroyEquirectHelper(getNativeObject()); + mNativeObject = 0; + } - HelperFinalizer(long nativeObject) { mNativeObject = nativeObject; } - - @Override - public void finalize() { - try { - super.finalize(); - } catch (Throwable t) { // Ignore - } finally { - nDestroyEquirectHelper(mNativeObject); - } + protected long getNativeObject() { + if (mNativeObject == 0) { + throw new IllegalStateException("Calling method on destroyed EquirectangularToCubemap"); } + return mNativeObject; } } public static class SpecularFilter { - @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources - private final HelperFinalizer mFinalizer; - private final long mNativeHelper; + private long mNativeObject; public SpecularFilter(IBLPrefilterContext context) { - mNativeHelper = nCreateSpecularFilter(context.mNativeObject); - mFinalizer = new HelperFinalizer(mNativeHelper); + mNativeObject = nCreateSpecularFilter(context.getNativeObject()); } public Texture run(Texture skybox) { - long nativeTexture = nSpecularFilterRun(mNativeHelper, skybox.getNativeObject()); + long nativeTexture = nSpecularFilterRun(getNativeObject(), skybox.getNativeObject()); return new Texture(nativeTexture); } - private static class HelperFinalizer { - private final long mNativeObject; - - HelperFinalizer(long nativeObject) { mNativeObject = nativeObject; } - - @Override - public void finalize() { - try { - super.finalize(); - } catch (Throwable t) { // Ignore - } finally { - nDestroySpecularFilter(mNativeObject); - } - } + public void destroy() { + nDestroySpecularFilter(getNativeObject()); + mNativeObject = 0; } + + protected long getNativeObject() { + if (mNativeObject == 0) { + throw new IllegalStateException("Calling method on destroyed SpecularFilter"); + } + return mNativeObject; + } + } + + + protected long getNativeObject() { + if (mNativeObject == 0) { + throw new IllegalStateException("Calling method on destroyed IBLPrefilterContext"); + } + return mNativeObject; } private static native long nCreate(long nativeEngine); diff --git a/android/gradle.properties b/android/gradle.properties index 199b188770..ccd8c511af 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.12.5 +VERSION_NAME=1.12.6 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/android/samples/sample-gltf-viewer/build.gradle b/android/samples/sample-gltf-viewer/build.gradle index ec503fccf7..2e1230aac6 100644 --- a/android/samples/sample-gltf-viewer/build.gradle +++ b/android/samples/sample-gltf-viewer/build.gradle @@ -10,6 +10,7 @@ filamentTools { iblOutputDir = project.layout.projectDirectory.dir("src/main/assets/envs") } +// don't forget to update MainACtivity.kt when/if changing this. task copyMesh(type: Copy) { from "../../../third_party/models/BusterDrone" into "src/main/assets/models" @@ -36,5 +37,5 @@ dependencies { implementation project(':filament-android') implementation project(':gltfio-android') implementation project(':filament-utils-android') - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0' } diff --git a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt index 2f69d02776..76e1b0f428 100644 --- a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt +++ b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt @@ -196,6 +196,10 @@ class MainActivity : Activity() { val sky = Skybox.Builder().environment(skyboxTexture).build(engine) + specularFilter.destroy(); + equirectToCubemap.destroy(); + context.destroy(); + modelViewer.scene.skybox = sky modelViewer.scene.indirectLight = ibl } diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index ef6b271346..ebe5e2879c 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -379,6 +379,7 @@ if (APPLE) test/test_BufferUpdates.cpp test/test_MRT.cpp test/test_LoadImage.cpp + test/test_RenderExternalImage.cpp ) target_link_libraries(backend_test PRIVATE diff --git a/filament/backend/src/metal/MetalBlitter.mm b/filament/backend/src/metal/MetalBlitter.mm index 0226a67166..219649894b 100644 --- a/filament/backend/src/metal/MetalBlitter.mm +++ b/filament/backend/src/metal/MetalBlitter.mm @@ -20,13 +20,6 @@ #include -#define NSERROR_CHECK(message) \ - if (error) { \ - auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; \ - utils::slog.e << description << utils::io::endl; \ - } \ - ASSERT_POSTCONDITION(error == nil, message); - namespace filament { namespace backend { namespace metal { @@ -477,7 +470,14 @@ id MetalBlitter::compileFragmentFunction(BlitFunctionKey key) { options:options error:&error]; id function = [library newFunctionWithName:@"blitterFrag"]; - NSERROR_CHECK("Unable to compile shading library for MetalBlitter."); + + if (!library || !function) { + if (error) { + auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; + utils::slog.e << description << utils::io::endl; + } + } + ASSERT_POSTCONDITION(library && function, "Unable to compile fragment shader for MetalBlitter."); return function; } @@ -494,7 +494,14 @@ id MetalBlitter::getBlitVertexFunction() { options:nil error:&error]; id function = [library newFunctionWithName:@"blitterVertex"]; - NSERROR_CHECK("Unable to compile shading library for MetalBlitter."); + + if (!library || !function) { + if (error) { + auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; + utils::slog.e << description << utils::io::endl; + } + } + ASSERT_POSTCONDITION(library && function, "Unable to compile vertex shader for MetalBlitter."); mVertexFunction = function; diff --git a/filament/backend/src/metal/MetalExternalImage.h b/filament/backend/src/metal/MetalExternalImage.h index f5ad259999..618da6efcc 100644 --- a/filament/backend/src/metal/MetalExternalImage.h +++ b/filament/backend/src/metal/MetalExternalImage.h @@ -100,6 +100,7 @@ private: CVMetalTextureRef createTextureFromImage(CVPixelBufferRef image, MTLPixelFormat format, size_t plane); id createRgbTexture(size_t width, size_t height); + id createSwizzledTextureView(id texture) const; id createSwizzledTextureView(CVMetalTextureRef texture) const; void ensureComputePipelineState(); id encodeColorConversionPass(id inYPlane, id @@ -125,7 +126,7 @@ private: struct { TextureSwizzle r, g, b, a; - } swizzle; + } mSwizzle; }; } // namespace metal diff --git a/filament/backend/src/metal/MetalExternalImage.mm b/filament/backend/src/metal/MetalExternalImage.mm index 8f2ac737c1..8cb539bd71 100644 --- a/filament/backend/src/metal/MetalExternalImage.mm +++ b/filament/backend/src/metal/MetalExternalImage.mm @@ -72,7 +72,7 @@ ycbcrToRgb(texture2d inYTexture [[texture(0)]], )"; MetalExternalImage::MetalExternalImage(MetalContext& context, TextureSwizzle r, TextureSwizzle g, - TextureSwizzle b, TextureSwizzle a) noexcept : mContext(context), swizzle{r, g, b ,a} { } + TextureSwizzle b, TextureSwizzle a) noexcept : mContext(context), mSwizzle{r, g, b, a} { } bool MetalExternalImage::isValid() const noexcept { return mRgbTexture != nil || mImage != nullptr; @@ -113,13 +113,12 @@ void MetalExternalImage::set(CVPixelBufferRef image) noexcept { mHeight = CVPixelBufferGetHeightOfPlane(image, Y_PLANE); id rgbTexture = createRgbTexture(mWidth, mHeight); - id commandBuffer = encodeColorConversionPass( + id commandBuffer = encodeColorConversionPass( CVMetalTextureGetTexture(yPlane), CVMetalTextureGetTexture(cbcrPlane), rgbTexture); - mRgbTexture = createTextureViewWithSwizzle(rgbTexture, - getSwizzleChannels(swizzle.r, swizzle.g, swizzle.b, swizzle.a)); + mRgbTexture = createSwizzledTextureView(rgbTexture); [commandBuffer addCompletedHandler:^(id o) { CVBufferRelease(yPlane); @@ -230,24 +229,28 @@ id MetalExternalImage::createRgbTexture(size_t width, size_t height) return [mContext.device newTextureWithDescriptor:descriptor]; } -id MetalExternalImage::createSwizzledTextureView(CVMetalTextureRef ref) const { - id texture = CVMetalTextureGetTexture(ref); +id MetalExternalImage::createSwizzledTextureView(id texture) const { const bool isDefaultSwizzle = - swizzle.r == TextureSwizzle::CHANNEL_0 && - swizzle.g == TextureSwizzle::CHANNEL_1 && - swizzle.b == TextureSwizzle::CHANNEL_2 && - swizzle.a == TextureSwizzle::CHANNEL_3; + mSwizzle.r == TextureSwizzle::CHANNEL_0 && + mSwizzle.g == TextureSwizzle::CHANNEL_1 && + mSwizzle.b == TextureSwizzle::CHANNEL_2 && + mSwizzle.a == TextureSwizzle::CHANNEL_3; if (!isDefaultSwizzle && mContext.supportsTextureSwizzling) { // Even though we've already checked supportsTextureSwizzling, we still need to guard these // calls with @availability, otherwise the API usage will generate compiler warnings. if (@available(iOS 13, *)) { texture = createTextureViewWithSwizzle(texture, - getSwizzleChannels(swizzle.r, swizzle.g, swizzle.b, swizzle.a)); + getSwizzleChannels(mSwizzle.r, mSwizzle.g, mSwizzle.b, mSwizzle.a)); } } return texture; } +id MetalExternalImage::createSwizzledTextureView(CVMetalTextureRef ref) const { + id texture = CVMetalTextureGetTexture(ref); + return createSwizzledTextureView(texture); +} + void MetalExternalImage::ensureComputePipelineState() { if (mContext.externalImageComputePipelineState != nil) { return; diff --git a/filament/backend/src/opengl/CocoaTouchExternalImage.h b/filament/backend/src/opengl/CocoaTouchExternalImage.h index d026d35cb2..7b68c758eb 100644 --- a/filament/backend/src/opengl/CocoaTouchExternalImage.h +++ b/filament/backend/src/opengl/CocoaTouchExternalImage.h @@ -63,7 +63,7 @@ private: void release() noexcept; CVOpenGLESTextureRef createTextureFromImage(CVPixelBufferRef image, GLuint glFormat, - size_t plane) noexcept; + GLenum format, size_t plane) noexcept; GLuint encodeColorConversionPass(GLuint yPlaneTexture, GLuint colorTexture, size_t width, size_t height) noexcept; diff --git a/filament/backend/src/opengl/CocoaTouchExternalImage.mm b/filament/backend/src/opengl/CocoaTouchExternalImage.mm index 7f79520b1b..bf0fc1a979 100644 --- a/filament/backend/src/opengl/CocoaTouchExternalImage.mm +++ b/filament/backend/src/opengl/CocoaTouchExternalImage.mm @@ -150,13 +150,14 @@ bool CocoaTouchExternalImage::set(CVPixelBufferRef image) noexcept { if (planeCount == 0) { mImage = image; - mTexture = createTextureFromImage(image, GL_RGBA, 0); + mTexture = createTextureFromImage(image, GL_RGBA, GL_BGRA, 0); mEncodedToRgb = false; } if (planeCount == 2) { - CVOpenGLESTextureRef yPlane = createTextureFromImage(image, GL_LUMINANCE, 0); - CVOpenGLESTextureRef colorPlane = createTextureFromImage(image, GL_LUMINANCE_ALPHA, 1); + CVOpenGLESTextureRef yPlane = createTextureFromImage(image, GL_LUMINANCE, GL_LUMINANCE, 0); + CVOpenGLESTextureRef colorPlane = createTextureFromImage(image, GL_LUMINANCE_ALPHA, + GL_LUMINANCE_ALPHA, 1); size_t width, height; width = CVPixelBufferGetWidthOfPlane(image, 0); @@ -214,7 +215,7 @@ void CocoaTouchExternalImage::release() noexcept { } CVOpenGLESTextureRef CocoaTouchExternalImage::createTextureFromImage(CVPixelBufferRef image, GLuint - glFormat, size_t plane) noexcept { + glFormat, GLenum format, size_t plane) noexcept { const size_t width = CVPixelBufferGetWidthOfPlane(image, plane); const size_t height = CVPixelBufferGetHeightOfPlane(image, plane); @@ -222,7 +223,7 @@ CVOpenGLESTextureRef CocoaTouchExternalImage::createTextureFromImage(CVPixelBuff UTILS_UNUSED_IN_RELEASE CVReturn success = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, mTextureCache, image, nullptr, GL_TEXTURE_2D, glFormat, width, height, - glFormat, GL_UNSIGNED_BYTE, plane, &texture); + format, GL_UNSIGNED_BYTE, plane, &texture); assert_invariant(success == kCVReturnSuccess); return texture; diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index 8db6bddfb3..9126675b15 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -54,8 +54,8 @@ void VulkanContext::selectPhysicalDevice() { } // Does the device have any command queues that support graphics? - // In theory we should also ensure that the device supports presentation of our - // particular VkSurface, but we don't have a VkSurface yet so we'll skip this requirement. + // In theory, we should also ensure that the device supports presentation of our + // particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement. uint32_t queueFamiliesCount; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, nullptr); if (queueFamiliesCount == 0) { diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 3cc5f74645..035924e12d 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -939,7 +939,8 @@ bool VulkanDriver::getTimerQueryValue(Handle tqh, uint64_t* elapse // However there are plans for implementing this properly. See the following GitHub ticket. // https://github.com/KhronosGroup/MoltenVK/issues/773 - uint64_t delta = timestamp1 - timestamp0; + float period = mContext.physicalDeviceProperties.limits.timestampPeriod; + uint64_t delta = uint64_t(float(timestamp1 - timestamp0) * period); *elapsedTime = delta; return true; } diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp new file mode 100644 index 0000000000..632ecfc50a --- /dev/null +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -0,0 +1,251 @@ +/* + * 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. + */ + +#include "BackendTest.h" + +#include "ShaderGenerator.h" +#include "TrianglePrimitive.h" + +#include + +namespace { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Shaders +//////////////////////////////////////////////////////////////////////////////////////////////////// + +std::string vertex (R"(#version 450 core + +layout(location = 0) in vec4 mesh_position; +layout(location = 0) out vec2 uv; + +void main() { + gl_Position = vec4(mesh_position.xy, 0.0, 1.0); + uv = (mesh_position.xy * 0.5 + 0.5); +} +)"); + +std::string fragment (R"(#version 450 core + +layout(location = 0) out vec4 fragColor; +layout(location = 0) in vec2 uv; + +layout(set = 1, binding = 6) uniform sampler2D tex; + +void main() { + fragColor = texture(tex, uv); +} +)"); + +} + +namespace test { + +using namespace filament; +using namespace filament::backend; + +// Rendering an external image without setting any data should not crash. +TEST_F(BackendTest, RenderExternalImageWithoutSet) { + TrianglePrimitive triangle(getDriverApi()); + + auto swapChain = createSwapChain(); + + ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); + + // Create a program that samples a texture. + Program p = shaderGen.getProgram(); + Program::Sampler sampler { utils::CString("tex"), 6 }; + p.setSamplerGroup(0, &sampler, 1); + backend::Handle program = getDriverApi().createProgram(std::move(p)); + + backend::Handle defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + + // Create a texture that will be backed by an external image. + auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; + const NativeView& view = getNativeView(); + backend::Handle texture = getDriverApi().createTexture( + SamplerType::SAMPLER_EXTERNAL, // target + 1, // levels + TextureFormat::RGBA8, // format + 1, // samples + view.width, // width + view.height, // height + 1, // depth + usage); // usage + + RenderPassParams params = {}; + fullViewport(params); + params.flags.clear = TargetBufferFlags::COLOR; + params.clearColor = {0.f, 1.f, 0.f, 1.f}; + params.flags.discardStart = TargetBufferFlags::ALL; + params.flags.discardEnd = TargetBufferFlags::NONE; + + PipelineState state; + state.program = program; + state.rasterState.colorWrite = true; + state.rasterState.depthWrite = false; + state.rasterState.depthFunc = RasterState::DepthFunc::A; + state.rasterState.culling = CullingMode::NONE; + + getDriverApi().startCapture(0); + getDriverApi().makeCurrent(swapChain, swapChain); + getDriverApi().beginFrame(0, 0); + + SamplerGroup mSamplers(1); + mSamplers.setSampler(0, { texture, {} }); + backend::Handle samplerGroup = getDriverApi().createSamplerGroup(1); + getDriverApi().updateSamplerGroup(samplerGroup, std::move(mSamplers.toCommandStream())); + getDriverApi().bindSamplers(0, samplerGroup); + + // Render a triangle. + getDriverApi().beginRenderPass(defaultRenderTarget, params); + getDriverApi().draw(state, triangle.getRenderPrimitive()); + getDriverApi().endRenderPass(); + + getDriverApi().flush(); + getDriverApi().commit(swapChain); + getDriverApi().endFrame(0); + + getDriverApi().stopCapture(0); + + // Delete our resources. + getDriverApi().destroyTexture(texture); + getDriverApi().destroySamplerGroup(samplerGroup); + + // Destroy frame resources. + getDriverApi().destroyProgram(program); + getDriverApi().destroyRenderTarget(defaultRenderTarget); + + executeCommands(); +} + +TEST_F(BackendTest, RenderExternalImage) { + TrianglePrimitive triangle(getDriverApi()); + + auto swapChain = createSwapChain(); + + ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); + + // Create a program that samples a texture. + Program p = shaderGen.getProgram(); + Program::Sampler sampler { utils::CString("tex"), 6 }; + p.setSamplerGroup(0, &sampler, 1); + auto program = getDriverApi().createProgram(std::move(p)); + + backend::Handle defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + + // require users to create two Filament textures and have two material parameters + // add a "plane" parameter to setExternalImage + + // Create a texture that will be backed by an external image. + auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; + const NativeView& view = getNativeView(); + backend::Handle texture = getDriverApi().createTexture( + SamplerType::SAMPLER_EXTERNAL, // target + 1, // levels + TextureFormat::RGBA8, // format + 1, // samples + view.width, // width + view.height, // height + 1, // depth + usage); // usage + + // Create an external image. + CFStringRef keys[4]; + keys[0] = kCVPixelBufferCGBitmapContextCompatibilityKey; + keys[1] = kCVPixelBufferCGImageCompatibilityKey; + keys[2] = kCVPixelBufferOpenGLCompatibilityKey; + keys[3] = kCVPixelBufferMetalCompatibilityKey; + CFTypeRef values[4]; + int yes = 1; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &yes); + values[1] = values[0]; + values[2] = values[0]; + values[3] = values[0]; + CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 4, nullptr, nullptr); + CVPixelBufferRef pixBuffer = nullptr; + CVReturn status = + CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, &pixBuffer); + assert(status == kCVReturnSuccess); + + // Fill image with checker-pattern. + const size_t tileSize = 64; + const uint32_t blue = 0xFF0000FF; // BGRA format + const uint32_t black = 0xFF000000; + CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixBuffer, 0); + assert(lockStatus == kCVReturnSuccess); + uint32_t* pix = (uint32_t*) CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0); + assert(pix); + for (size_t r = 0; r < 1024; r++) { + for (size_t c = 0; c < 1024; c++) { + size_t idx = r * 1024 + c; + pix[idx] = (idx + tileSize * (r / tileSize % 2)) / tileSize % 2 == 0 ? blue : black; + } + } + + getDriverApi().setupExternalImage(pixBuffer); + getDriverApi().setExternalImage(texture, pixBuffer); + + // We're now free to release the buffer. + CVBufferRelease(pixBuffer); + + RenderPassParams params = {}; + fullViewport(params); + params.flags.clear = TargetBufferFlags::COLOR; + params.clearColor = {0.f, 1.f, 0.f, 1.f}; + params.flags.discardStart = TargetBufferFlags::ALL; + params.flags.discardEnd = TargetBufferFlags::NONE; + + PipelineState state; + state.program = program; + state.rasterState.colorWrite = true; + state.rasterState.depthWrite = false; + state.rasterState.depthFunc = RasterState::DepthFunc::A; + state.rasterState.culling = CullingMode::NONE; + + getDriverApi().startCapture(0); + getDriverApi().makeCurrent(swapChain, swapChain); + getDriverApi().beginFrame(0, 0); + + SamplerGroup mSamplers(1); + mSamplers.setSampler(0, { texture, {} }); + backend::Handle samplerGroup = getDriverApi().createSamplerGroup(1); + getDriverApi().updateSamplerGroup(samplerGroup, std::move(mSamplers.toCommandStream())); + getDriverApi().bindSamplers(0, samplerGroup); + + // Render a triangle. + getDriverApi().beginRenderPass(defaultRenderTarget, params); + getDriverApi().draw(state, triangle.getRenderPrimitive()); + getDriverApi().endRenderPass(); + + getDriverApi().flush(); + getDriverApi().commit(swapChain); + getDriverApi().endFrame(0); + + getDriverApi().stopCapture(0); + + // Delete our resources. + getDriverApi().destroyTexture(texture); + getDriverApi().destroySamplerGroup(samplerGroup); + + // Destroy frame resources. + getDriverApi().destroyProgram(program); + getDriverApi().destroyRenderTarget(defaultRenderTarget); + + executeCommands(); +} + +} // namespace test diff --git a/filament/include/filament/MaterialInstance.h b/filament/include/filament/MaterialInstance.h index 98b526811f..fee1ca0d28 100644 --- a/filament/include/filament/MaterialInstance.h +++ b/filament/include/filament/MaterialInstance.h @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -37,6 +39,7 @@ class UniformInterfaceBlock; class UTILS_PUBLIC MaterialInstance : public FilamentAPI { public: using CullingMode = filament::backend::CullingMode; + using TransparencyMode = filament::TransparencyMode; template using is_supported_parameter_t = typename std::enable_if< @@ -197,6 +200,11 @@ public: */ void setDoubleSided(bool doubleSided) noexcept; + /** + * Specifies how transparent objects should be rendered (default is DEFAULT). + */ + void setTransparencyMode(TransparencyMode mode) noexcept; + /** * Overrides the default triangle culling state that was set on the material. */ diff --git a/filament/src/MaterialInstance.cpp b/filament/src/MaterialInstance.cpp index caca664830..7fc9a8e648 100644 --- a/filament/src/MaterialInstance.cpp +++ b/filament/src/MaterialInstance.cpp @@ -204,6 +204,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, mMaterialSortingKey = RenderPass::makeMaterialSortingKey( material->getId(), material->generateMaterialInstanceId()); + + setTransparencyMode(material->getTransparencyMode()); } FMaterialInstance* FMaterialInstance::duplicate( @@ -253,6 +255,8 @@ void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* ma setSpecularAntiAliasingVariance(material->getSpecularAntiAliasingVariance()); setSpecularAntiAliasingThreshold(material->getSpecularAntiAliasingThreshold()); } + + setTransparencyMode(material->getTransparencyMode()); } FMaterialInstance::~FMaterialInstance() noexcept = default; @@ -327,6 +331,10 @@ void FMaterialInstance::setDoubleSided(bool doubleSided) noexcept { } } +void FMaterialInstance::setTransparencyMode(TransparencyMode mode) noexcept { + mTransparencyMode = mode; +} + void FMaterialInstance::setDepthCulling(bool enable) noexcept { mDepthFunc = enable ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A; } @@ -393,6 +401,10 @@ void MaterialInstance::setDoubleSided(bool doubleSided) noexcept { upcast(this)->setDoubleSided(doubleSided); } +void MaterialInstance::setTransparencyMode(TransparencyMode mode) noexcept { + upcast(this)->setTransparencyMode(mode); +} + void MaterialInstance::setCullingMode(CullingMode culling) noexcept { upcast(this)->setCullingMode(culling); } diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index 947e3f96bf..aa7f032f7c 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -87,6 +87,16 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept { s.clipControl = mClipControl; } +void PerViewUniforms::prepareUpscaler(math::float2 scale, + DynamicResolutionOptions const& options) noexcept { + auto& s = mPerViewUb.edit(); + if (options.quality >= QualityLevel::HIGH) { + s.lodBias = std::log2(std::min(scale.x, scale.y)); + } else { + s.lodBias = 0.0f; + } +} + void PerViewUniforms::prepareExposure(float ev100) noexcept { const float exposure = Exposure::exposure(ev100); auto& s = mPerViewUb.edit(); diff --git a/filament/src/PerViewUniforms.h b/filament/src/PerViewUniforms.h index 9173ff4811..bc24e7a264 100644 --- a/filament/src/PerViewUniforms.h +++ b/filament/src/PerViewUniforms.h @@ -31,6 +31,7 @@ namespace filament { struct FogOptions; +struct DynamicResolutionOptions; struct AmbientOcclusionOptions; struct VsmShadowOptions; @@ -53,6 +54,7 @@ public: void terminate(FEngine& engine); void prepareCamera(const CameraInfo& camera) noexcept; + void prepareUpscaler(math::float2 scale, DynamicResolutionOptions const& options) noexcept; void prepareViewport(const filament::Viewport& viewport) noexcept; void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept; void prepareExposure(float ev100) noexcept; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 37048489d8..472869dfe7 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -392,7 +392,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags, cmdColor.key |= makeField(primitive.getBlendOrder(), BLEND_ORDER_MASK, BLEND_ORDER_SHIFT); - const TransparencyMode mode = mi->getMaterial()->getTransparencyMode(); + const TransparencyMode mode = mi->getTransparencyMode(); // handle transparent objects, two techniques: // diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 71b7112c6c..fb28d7f4be 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -258,6 +258,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { view.prepare(engine, driver, arena, svp, getShaderUserTime()); + view.prepareUpscaler(scale); + // start froxelization immediately, it has no dependencies JobSystem::Job* jobFroxelize = nullptr; if (view.hasDynamicLighting()) { diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 876528281a..95c5271639 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -554,6 +554,11 @@ UTILS_NOINLINE }); } +void FView::prepareUpscaler(float2 scale) const noexcept { + SYSTRACE_CALL(); + mPerViewUniforms.prepareUpscaler(scale, mDynamicResolution); +} + void FView::prepareCamera(const CameraInfo& camera) const noexcept { SYSTRACE_CALL(); mPerViewUniforms.prepareCamera(camera); diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 51d36baf51..64f1825545 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -45,7 +45,7 @@ struct LightManager::BuilderDetails { float mIntensity = 100000.0f; FLightManager::IntensityUnit mIntensityUnit = FLightManager::IntensityUnit::LUMEN_LUX; float3 mDirection = { 0.0f, -1.0f, 0.0f }; - float2 mSpotInnerOuter = { f::PI, f::PI }; + float2 mSpotInnerOuter = { f::PI_4 * 0.75f, f::PI_4 }; float mSunAngle = 0.00951f; // 0.545° in radians float mSunHaloSize = 10.0f; float mSunHaloFalloff = 80.0f; diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h index 9c996130e0..be564257b2 100644 --- a/filament/src/details/MaterialInstance.h +++ b/filament/src/details/MaterialInstance.h @@ -91,6 +91,8 @@ public: bool getDepthWrite() const noexcept { return mDepthWrite; } + TransparencyMode getTransparencyMode() const noexcept { return mTransparencyMode; } + backend::RasterState::DepthFunc getDepthFunc() const noexcept { return mDepthFunc; } void setPolygonOffset(float scale, float constant) noexcept { @@ -108,6 +110,8 @@ public: void setDoubleSided(bool doubleSided) noexcept; + void setTransparencyMode(TransparencyMode mode) noexcept; + void setCullingMode(CullingMode culling) noexcept { mCulling = culling; } void setColorWrite(bool enable) noexcept { mColorWrite = enable; } @@ -159,6 +163,7 @@ private: bool mColorWrite; bool mDepthWrite; backend::RasterState::DepthFunc mDepthFunc; + TransparencyMode mTransparencyMode; uint64_t mMaterialSortingKey = 0; diff --git a/filament/src/details/View.h b/filament/src/details/View.h index fd78e465ff..c2d4066ddb 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -159,6 +159,7 @@ public: return mName.c_str(); } + void prepareUpscaler(math::float2 scale) const noexcept; void prepareCamera(const CameraInfo& camera) const noexcept; void prepareViewport(const Viewport& viewport) const noexcept; void prepareShadowing(FEngine& engine, backend::DriverApi& driver, diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 351b147c13..7c96458a3c 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.12.5" + spec.version = "1.12.6" 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.12.5/filament-v1.12.5-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.12.6/filament-v1.12.6-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { @@ -83,9 +83,15 @@ Pod::Spec.new do |spec| end spec.subspec "utils" do |ss| - ss.source_files = "include/utils/*.h" + ss.source_files = "include/utils/**/*.h" + ss.header_mappings_dir = "include" ss.vendored_libraries = "lib/universal/libutils.a" - ss.header_dir = "utils" + ss.dependency "Filament/tsl" + end + + spec.subspec "tsl" do |ss| + ss.source_files = "include/tsl/*.h" + ss.header_dir = "tsl" end spec.subspec "math" do |ss| diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index b42787246d..e61a4e5ec5 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -128,8 +128,13 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float vsmLightBleedReduction; float vsmReserved0; + float lodBias; + float reserved1; + float reserved2; + float reserved3; + // bring PerViewUib to 2 KiB - math::float4 padding2[59]; + math::float4 padding2[58]; }; // 2 KiB == 128 float4s diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index b2eb53c1dd..6c9de50f25 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -30,6 +30,7 @@ #include "sca/GLSLTools.h" #include +#include using namespace glslang; using namespace spirv_cross; @@ -203,6 +204,12 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co return false; } + // add texture lod bias + if (config.shaderType == filament::backend::FRAGMENT && + config.domain == filament::MaterialDomain::SURFACE) { + GLSLTools::textureLodBias(tShader); + } + program.addShader(&tShader); // Even though we only have a single shader stage, linking is still necessary to finalize // SPIR-V types diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 8096e8a098..dd9e749102 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -50,6 +50,7 @@ public: struct Config { filament::backend::ShaderType shaderType; filament::backend::ShaderModel shaderModel; + filament::MaterialDomain domain; bool hasFramebufferFetch; struct { std::vector> subpassInputToColorLocation; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 71c21d8181..194cdca168 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -726,7 +726,8 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector +class TraverserAdapter: public TIntermTraverser { + F closure; +public: + explicit TraverserAdapter(F closure) + : TIntermTraverser(true, false, false, false), + closure(closure) { + } + bool visitAggregate(TVisit visit, TIntermAggregate* node) override { + return closure(visit, node); + } +}; + +void textureLodBias(TIntermediate* intermediate, TIntermNode* root, + const char* entryPointSignatureish, const char* lodBiasSymbolName) { + + // First, find the "lodBias" symbol and entry point + const std::string functionName{ entryPointSignatureish }; + TIntermSymbol* pIntermSymbolLodBias = nullptr; + TIntermNode* pEntryPointRoot = nullptr; + TraverserAdapter findLodBiasSymbol( + [&](TVisit visit, TIntermAggregate* node) { + if (node->getOp() == glslang::EOpSequence) { + return true; + } + if (node->getOp() == glslang::EOpFunction) { + if (node->getName().rfind(functionName, 0) == 0) { + pEntryPointRoot = node; + } + return false; + } + if (node->getOp() == glslang::EOpLinkerObjects) { + for (TIntermNode* item: node->getSequence()) { + TIntermSymbol* symbol = item->getAsSymbolNode(); + if (symbol && symbol->getBasicType() == TBasicType::EbtFloat) { + if (symbol->getName() == lodBiasSymbolName) { + pIntermSymbolLodBias = symbol; + break; + } + } + } + } + return true; + }); + root->traverse(&findLodBiasSymbol); + + if (!pEntryPointRoot) { + // This can happen if the material doesn't have user defined code, + // e.g. with the depth material. We just do nothing then. + return; + } + + if (!pIntermSymbolLodBias) { + // something went wrong + utils::slog.e << "lod bias ignored because \"" << lodBiasSymbolName << "\" was not found!" + << utils::io::endl; + return; + } + + // add lod bias to texture calls + TraverserAdapter addLodBiasToTextureCalls( + [&](TVisit visit, TIntermAggregate* node) { + // skip everything that's not a texture() call + if (node->getOp() != glslang::EOpTexture) { + return true; + } + + TIntermSequence& sequence = node->getSequence(); + + // first check that we have the correct sampler + TIntermTyped* pTyped = sequence[0]->getAsTyped(); + if (!pTyped) { + return false; + } + + TSampler const& sampler = pTyped->getType().getSampler(); + if (sampler.isArrayed() && sampler.isShadow()) { + // sampler2DArrayShadow is not supported + return false; + } + + // Then add the lod bias to the texture() call + if (sequence.size() == 2) { + // we only have 2 parameters, add the 3rd one + TIntermSymbol* symbol = intermediate->addSymbol(*pIntermSymbolLodBias); + sequence.push_back(symbol); + } else if (sequence.size() == 3) { + // load bias is already specified + TIntermSymbol* symbol = intermediate->addSymbol(*pIntermSymbolLodBias); + TIntermTyped* pAdd = intermediate->addBinaryMath(TOperator::EOpAdd, + sequence[2]->getAsTyped(), symbol, + node->getLoc()); + sequence[2] = pAdd; + } + + return false; + }); + // we need to run this only from the user's main entry point + pEntryPointRoot->traverse(&addLodBiasToTextureCalls); +} + } // namespace ASTHelpers diff --git a/libs/filamat/src/sca/ASTHelpers.h b/libs/filamat/src/sca/ASTHelpers.h index 7fb11eaccf..25a12bf6b3 100644 --- a/libs/filamat/src/sca/ASTHelpers.h +++ b/libs/filamat/src/sca/ASTHelpers.h @@ -71,5 +71,10 @@ struct FunctionParameter { void getFunctionParameters(glslang::TIntermAggregate* func, std::vector& output) noexcept; +// add lod bias to texture() calls +void textureLodBias(glslang::TIntermediate* intermediate, TIntermNode* root, + const char* entryPointSignatureish, const char* lodBiasSymbolName); + + } // namespace ASTutils #endif //TNT_SCAHELPERS_H_H diff --git a/libs/filamat/src/sca/GLSLTools.cpp b/libs/filamat/src/sca/GLSLTools.cpp index f439d0921a..ae79c71ce5 100644 --- a/libs/filamat/src/sca/GLSLTools.cpp +++ b/libs/filamat/src/sca/GLSLTools.cpp @@ -359,4 +359,12 @@ void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslan } } +void GLSLTools::textureLodBias(TShader& shader) { + TIntermediate* intermediate = shader.getIntermediate(); + TIntermNode* root = intermediate->getTreeRoot(); + ASTUtils::textureLodBias(intermediate, root, + "material(struct-MaterialInputs", + "filament_lodBias"); +} + } // namespace filamat diff --git a/libs/filamat/src/sca/GLSLTools.h b/libs/filamat/src/sca/GLSLTools.h index e37bb60a39..e9908ff54c 100644 --- a/libs/filamat/src/sca/GLSLTools.h +++ b/libs/filamat/src/sca/GLSLTools.h @@ -150,6 +150,8 @@ public: static void prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslang::TShader& shader, EShLanguage language, int version, MaterialBuilder::Optimization optimization); + static void textureLodBias(glslang::TShader& shader); + private: diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index 7d6dfdf397..efb08bd55b 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -434,6 +434,8 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE), material.sib); + fs << "float filament_lodBias;\n"; + // shading code cg.generateCommon(fs, ShaderType::FRAGMENT); cg.generateGetters(fs, ShaderType::FRAGMENT); diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index ff8b7046a2..c0493a4891 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -54,6 +54,7 @@ endif() function(generate_mat TEMPLATE SHADINGMODEL BLENDING) set(DOUBLESIDED false) + set(TRANSPARENCY default) set(GENERATED_MAT "${RESOURCE_DIR}/${SHADINGMODEL}_${BLENDING}.mat") configure_file(materials/${TEMPLATE}.mat.in ${GENERATED_MAT}) set(MATERIAL_SRCS ${MATERIAL_SRCS} ${GENERATED_MAT} PARENT_SCOPE) @@ -116,6 +117,7 @@ set(LITE_DIR ${CMAKE_CURRENT_BINARY_DIR}/lite) function(generate_lite_mat BLENDING) set(DOUBLESIDED false) + set(TRANSPARENCY default) set(SHADINGMODEL lit) set(GENERATED_MAT "${LITE_DIR}/${SHADINGMODEL}_${BLENDING}.mat") configure_file(materials/gltflite.mat.in ${GENERATED_MAT}) diff --git a/libs/gltfio/materials/gltflite.mat.in b/libs/gltfio/materials/gltflite.mat.in index 39813a0135..ea7a9cec7a 100644 --- a/libs/gltfio/materials/gltflite.mat.in +++ b/libs/gltfio/materials/gltflite.mat.in @@ -3,8 +3,8 @@ material { requires : [ uv0, uv1, color ], shadingModel : ${SHADINGMODEL}, blending : ${BLENDING}, - depthWrite : true, doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, diff --git a/libs/gltfio/materials/sheen.mat.in b/libs/gltfio/materials/sheen.mat.in index 19c32fc36b..e621e4b966 100644 --- a/libs/gltfio/materials/sheen.mat.in +++ b/libs/gltfio/materials/sheen.mat.in @@ -5,6 +5,7 @@ material { blending : fade, depthWrite : true, doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, diff --git a/libs/gltfio/materials/transmission.mat.in b/libs/gltfio/materials/transmission.mat.in index 656339535a..210bfe896d 100644 --- a/libs/gltfio/materials/transmission.mat.in +++ b/libs/gltfio/materials/transmission.mat.in @@ -4,6 +4,7 @@ material { shadingModel : ${SHADINGMODEL}, blending : masked, doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, diff --git a/libs/gltfio/materials/ubershader.mat.in b/libs/gltfio/materials/ubershader.mat.in index 1bee958d79..62fedd0ab6 100644 --- a/libs/gltfio/materials/ubershader.mat.in +++ b/libs/gltfio/materials/ubershader.mat.in @@ -3,8 +3,8 @@ material { requires : [ uv0, uv1, color ], shadingModel : ${SHADINGMODEL}, blending : ${BLENDING}, - depthWrite : true, doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, diff --git a/libs/gltfio/materials/volume.mat.in b/libs/gltfio/materials/volume.mat.in index e9d5f3f702..37baf7b233 100644 --- a/libs/gltfio/materials/volume.mat.in +++ b/libs/gltfio/materials/volume.mat.in @@ -4,6 +4,7 @@ material { shadingModel : ${SHADINGMODEL}, blending : masked, doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp index 54df0d9380..2f6aea933b 100644 --- a/libs/gltfio/src/MaterialGenerator.cpp +++ b/libs/gltfio/src/MaterialGenerator.cpp @@ -321,6 +321,9 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const .clearCoatIorChange(false) .material(shader.c_str()) .doubleSided(config.doubleSided) + .transparencyMode(config.doubleSided ? + MaterialBuilder::TransparencyMode::TWO_PASSES_TWO_SIDES : + MaterialBuilder::TransparencyMode::DEFAULT) .targetApi(filamat::targetApiFromBackend(engine->getBackend())); if (!optimizeShaders) { @@ -483,7 +486,6 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const break; case AlphaMode::BLEND: builder.blending(MaterialBuilder::BlendingMode::FADE); - builder.depthWrite(true); break; default: // Ignore diff --git a/libs/gltfio/src/UbershaderLoader.cpp b/libs/gltfio/src/UbershaderLoader.cpp index 2b70c20658..c7874462c1 100644 --- a/libs/gltfio/src/UbershaderLoader.cpp +++ b/libs/gltfio/src/UbershaderLoader.cpp @@ -208,6 +208,9 @@ MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, mi->setDoubleSided(config->doubleSided); mi->setCullingMode(config->doubleSided ? CullingMode::NONE : CullingMode::BACK); + mi->setTransparencyMode(config->doubleSided ? + MaterialInstance::TransparencyMode::TWO_PASSES_TWO_SIDES : + MaterialInstance::TransparencyMode::DEFAULT); #if !GLTFIO_LITE diff --git a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h index 560005f8c6..15cd681021 100644 --- a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h +++ b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h @@ -62,7 +62,7 @@ public: * Creates an IBLPrefilter context. * @param engine filament engine to use */ - IBLPrefilterContext(filament::Engine& engine); + explicit IBLPrefilterContext(filament::Engine& engine); /** * Destroys all GPU resources created during initialization. @@ -75,7 +75,7 @@ public: // movable IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept; - IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs); + IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs) noexcept; // ------------------------------------------------------------------------------------------- @@ -98,7 +98,7 @@ public: EquirectangularToCubemap(EquirectangularToCubemap const&) = delete; EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete; EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept; - EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs); + EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs) noexcept; /** * Converts an equirectangular image to a cubemap. @@ -175,7 +175,7 @@ public: SpecularFilter(SpecularFilter const&) = delete; SpecularFilter& operator=(SpecularFilter const&) = delete; SpecularFilter(SpecularFilter&& rhs) noexcept; - SpecularFilter& operator=(SpecularFilter&& rhs); + SpecularFilter& operator=(SpecularFilter&& rhs) noexcept; /** * Generates a prefiltered cubemap. diff --git a/libs/iblprefilter/src/IBLPrefilterContext.cpp b/libs/iblprefilter/src/IBLPrefilterContext.cpp index cc64634636..83fb60ef02 100644 --- a/libs/iblprefilter/src/IBLPrefilterContext.cpp +++ b/libs/iblprefilter/src/IBLPrefilterContext.cpp @@ -142,7 +142,7 @@ IBLPrefilterContext::IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept this->operator=(std::move(rhs)); } -IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) { +IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) noexcept { using std::swap; if (this != & rhs) { swap(mRenderer, rhs.mRenderer); @@ -182,7 +182,7 @@ IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap( IBLPrefilterContext::EquirectangularToCubemap& IBLPrefilterContext::EquirectangularToCubemap::operator=( - IBLPrefilterContext::EquirectangularToCubemap&& rhs) { + IBLPrefilterContext::EquirectangularToCubemap&& rhs) noexcept { using std::swap; if (this != &rhs) { swap(mEquirectMaterial, rhs.mEquirectMaterial); @@ -332,7 +332,8 @@ IBLPrefilterContext::SpecularFilter::SpecularFilter(SpecularFilter&& rhs) noexce this->operator=(std::move(rhs)); } -IBLPrefilterContext::SpecularFilter& IBLPrefilterContext::SpecularFilter::operator=(SpecularFilter&& rhs) { +IBLPrefilterContext::SpecularFilter& +IBLPrefilterContext::SpecularFilter::operator=(SpecularFilter&& rhs) noexcept { using std::swap; if (this != & rhs) { swap(mKernelMaterial, rhs.mKernelMaterial); diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 4d6d9c4076..0565ec6ca4 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -7,6 +7,8 @@ layout(location = 0) out vec4 fragColor; //------------------------------------------------------------------------------ void main() { + filament_lodBias = frameUniforms.lodBias; + #if defined(BLEND_MODE_MASKED) || (defined(BLEND_MODE_TRANSPARENT) && defined(HAS_TRANSPARENT_SHADOW)) MaterialInputs inputs; initMaterial(inputs); diff --git a/shaders/src/main.fs b/shaders/src/main.fs index 53be5174e3..fbe3601542 100644 --- a/shaders/src/main.fs +++ b/shaders/src/main.fs @@ -17,6 +17,8 @@ void blendPostLightingColor(const MaterialInputs material, inout vec4 color) { #endif void main() { + filament_lodBias = frameUniforms.lodBias; + // See shading_parameters.fs // Computes global variables we need to evaluate material and lighting computeShadingParams(); diff --git a/third_party/cgltf/LICENSE b/third_party/cgltf/LICENSE index 0afe8c71ad..599d9341a7 100644 --- a/third_party/cgltf/LICENSE +++ b/third_party/cgltf/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018 Johannes Kuhlmann +Copyright (c) 2018-2021 Johannes Kuhlmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/third_party/cgltf/README.md b/third_party/cgltf/README.md index 8b4c3deb1d..1a406c7235 100644 --- a/third_party/cgltf/README.md +++ b/third_party/cgltf/README.md @@ -3,7 +3,7 @@ [![Build Status](https://github.com/jkuhlmann/cgltf/workflows/build/badge.svg)](https://github.com/jkuhlmann/cgltf/actions) -Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), and more! +Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more! ## Usage: Loading Loading from file: diff --git a/third_party/cgltf/cgltf.h b/third_party/cgltf/cgltf.h index 4cc2d7ec93..7858697490 100644 --- a/third_party/cgltf/cgltf.h +++ b/third_party/cgltf/cgltf.h @@ -1,7 +1,7 @@ /** * cgltf - a single-file glTF 2.0 parser written in C99. * - * Version: 1.10 + * Version: 1.11 * * Website: https://github.com/jkuhlmann/cgltf * @@ -234,6 +234,12 @@ typedef enum cgltf_light_type { cgltf_light_type_spot, } cgltf_light_type; +typedef enum cgltf_data_free_method { + cgltf_data_free_method_none, + cgltf_data_free_method_file_release, + cgltf_data_free_method_memory_free, +} cgltf_data_free_method; + typedef struct cgltf_extras { cgltf_size start_offset; cgltf_size end_offset; @@ -250,6 +256,7 @@ typedef struct cgltf_buffer cgltf_size size; char* uri; void* data; /* loaded by cgltf_load_buffers */ + cgltf_data_free_method data_free_method; cgltf_extras extras; cgltf_size extensions_count; cgltf_extension* extensions; @@ -372,6 +379,8 @@ typedef struct cgltf_texture char* name; cgltf_image* image; cgltf_sampler* sampler; + cgltf_bool has_basisu; + cgltf_image* basisu_image; cgltf_extras extras; cgltf_size extensions_count; cgltf_extension* extensions; @@ -382,6 +391,7 @@ typedef struct cgltf_texture_transform cgltf_float offset[2]; cgltf_float rotation; cgltf_float scale[2]; + cgltf_bool has_texcoord; cgltf_int texcoord; } cgltf_texture_transform; @@ -595,6 +605,7 @@ typedef struct cgltf_light { cgltf_float range; cgltf_float spot_inner_cone_angle; cgltf_float spot_outer_cone_angle; + cgltf_extras extras; } cgltf_light; struct cgltf_node { @@ -813,7 +824,7 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* #include /* For UINT_MAX etc */ #include /* For FLT_MAX */ -#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) +#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) #include /* For malloc, free, atoi, atof */ #endif @@ -883,6 +894,9 @@ static const uint32_t GlbMagicBinChunk = 0x004E4942; #ifndef CGLTF_ATOF #define CGLTF_ATOF(str) atof(str) #endif +#ifndef CGLTF_ATOLL +#define CGLTF_ATOLL(str) atoll(str) +#endif #ifndef CGLTF_VALIDATE_ENABLE_ASSERTS #define CGLTF_VALIDATE_ENABLE_ASSERTS 0 #endif @@ -932,7 +946,12 @@ static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* m { fseek(file, 0, SEEK_END); +#ifdef _WIN32 + __int64 length = _ftelli64(file); +#else long length = ftell(file); +#endif + if (length < 0) { fclose(file); @@ -1120,8 +1139,8 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg return cgltf_result_invalid_options; } - void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; void* file_data = NULL; cgltf_size file_size = 0; @@ -1135,7 +1154,7 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg if (result != cgltf_result_success) { - memory_free(options->memory.user_data, file_data); + file_release(&options->memory, &options->file, file_data); return result; } @@ -1291,6 +1310,7 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, } data->buffers[0].data = (void*)data->bin; + data->buffers[0].data_free_method = cgltf_data_free_method_none; } for (cgltf_size i = 0; i < data->buffers_count; ++i) @@ -1314,6 +1334,7 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) { cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; if (res != cgltf_result_success) { @@ -1328,6 +1349,7 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, else if (strstr(uri, "://") == NULL && gltf_path) { cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_file_release; if (res != cgltf_result_success) { @@ -1655,10 +1677,15 @@ void cgltf_free(cgltf_data* data) { data->memory.free(data->memory.user_data, data->buffers[i].name); - if (data->buffers[i].data != data->bin) + if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) { file_release(&data->memory, &data->file, data->buffers[i].data); } + else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) + { + data->memory.free(data->memory.user_data, data->buffers[i].data); + } + data->memory.free(data->memory.user_data, data->buffers[i].uri); cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); @@ -2259,6 +2286,7 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size #define CGLTF_ERROR_LEGACY -3 #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; } #define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ #define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) @@ -2283,6 +2311,16 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) return CGLTF_ATOI(tmp); } +static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size); + char tmp[128]; + int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_size)CGLTF_ATOLL(tmp); +} + static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) { CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); @@ -3024,7 +3062,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) { ++i; - out_sparse->indices_byte_offset = cgltf_json_to_int(tokens + i, json_chunk); + out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) @@ -3073,7 +3111,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) { ++i; - out_sparse->values_byte_offset = cgltf_json_to_int(tokens + i, json_chunk); + out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) @@ -3142,7 +3180,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to { ++i; out_accessor->offset = - cgltf_json_to_int(tokens+i, json_chunk); + cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) @@ -3268,6 +3306,7 @@ static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, co else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) { ++i; + out_texture_transform->has_texcoord = 1; out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); ++i; } @@ -3885,7 +3924,62 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_texture->extensions_count, &out_texture->extensions); + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if (out_texture->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_texture->extensions_count = 0; + + if (!out_texture->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) + { + out_texture->has_basisu = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } } else { @@ -4192,19 +4286,19 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_ else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) { ++i; - out_meshopt_compression->offset = cgltf_json_to_int(tokens+i, json_chunk); + out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) { ++i; - out_meshopt_compression->size = cgltf_json_to_int(tokens+i, json_chunk); + out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) { ++i; - out_meshopt_compression->stride = cgltf_json_to_int(tokens+i, json_chunk); + out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) @@ -4290,21 +4384,21 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* { ++i; out_buffer_view->offset = - cgltf_json_to_int(tokens+i, json_chunk); + cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) { ++i; out_buffer_view->size = - cgltf_json_to_int(tokens+i, json_chunk); + cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) { ++i; out_buffer_view->stride = - cgltf_json_to_int(tokens+i, json_chunk); + cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) @@ -4422,7 +4516,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke { ++i; out_buffer->size = - cgltf_json_to_int(tokens+i, json_chunk); + cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) @@ -4737,6 +4831,14 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + out_light->color[0] = 1.f; + out_light->color[1] = 1.f; + out_light->color[2] = 1.f; + out_light->intensity = 1.f; + + out_light->spot_inner_cone_angle = 0.f; + out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; + int size = tokens[i].size; ++i; @@ -4817,6 +4919,10 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -5851,6 +5957,7 @@ static int cgltf_fixup_pointers(cgltf_data* data) for (cgltf_size i = 0; i < data->textures_count; ++i) { CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); } @@ -6305,7 +6412,7 @@ static void jsmn_init(jsmn_parser *parser) { /* cgltf is distributed under MIT license: * - * Copyright (c) 2018 Johannes Kuhlmann + * Copyright (c) 2018-2021 Johannes Kuhlmann * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/third_party/cgltf/cgltf_write.h b/third_party/cgltf/cgltf_write.h index 0b0162c9f3..8b96eb2b66 100644 --- a/third_party/cgltf/cgltf_write.h +++ b/third_party/cgltf/cgltf_write.h @@ -1,7 +1,7 @@ /** * cgltf_write - a single-file glTF 2.0 writer written in C99. * - * Version: 1.10 + * Version: 1.11 * * Website: https://github.com/jkuhlmann/cgltf * @@ -64,6 +64,7 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si #ifdef CGLTF_WRITE_IMPLEMENTATION +#include #include #include #include @@ -82,6 +83,7 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si #define CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN (1 << 9) #define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS (1 << 10) #define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME (1 << 11) +#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU (1 << 12) typedef struct { char* buffer; @@ -108,6 +110,7 @@ typedef struct { #endif #define CGLTF_SPRINTF(...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \ context->chars_written += context->tmp; \ if (context->cursor) { \ @@ -116,6 +119,7 @@ typedef struct { } } #define CGLTF_SNPRINTF(length, ...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \ context->chars_written += length; \ if (context->cursor) { \ @@ -229,6 +233,16 @@ static void cgltf_write_intprop(cgltf_write_context* context, const char* label, } } +static void cgltf_write_sizeprop(cgltf_write_context* context, const char* label, cgltf_size val, cgltf_size def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %zu", label, val); + context->needs_comma = 1; + } +} + static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def) { if (val != def) @@ -377,7 +391,10 @@ static void cgltf_write_texture_transform(cgltf_write_context* context, const cg { cgltf_write_floatarrayprop(context, "scale", transform->scale, 2); } - cgltf_write_intprop(context, "texCoord", transform->texcoord, 0); + if (transform->has_texcoord) + { + cgltf_write_intprop(context, "texCoord", transform->texcoord, -1); + } cgltf_write_line(context, "}"); cgltf_write_line(context, "}"); } @@ -500,9 +517,9 @@ static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_bu cgltf_write_line(context, "{"); cgltf_write_strprop(context, "name", view->name); CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers); - cgltf_write_intprop(context, "byteLength", (int)view->size, -1); - cgltf_write_intprop(context, "byteOffset", (int)view->offset, 0); - cgltf_write_intprop(context, "byteStride", (int)view->stride, 0); + cgltf_write_sizeprop(context, "byteLength", view->size, (cgltf_size)-1); + cgltf_write_sizeprop(context, "byteOffset", view->offset, 0); + cgltf_write_sizeprop(context, "byteStride", view->stride, 0); // NOTE: We skip writing "target" because the spec says its usage can be inferred. cgltf_write_extras(context, &view->extras); cgltf_write_line(context, "}"); @@ -514,7 +531,7 @@ static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* cgltf_write_line(context, "{"); cgltf_write_strprop(context, "name", buffer->name); cgltf_write_strprop(context, "uri", buffer->uri); - cgltf_write_intprop(context, "byteLength", (int)buffer->size, -1); + cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1); cgltf_write_extras(context, &buffer->extras); cgltf_write_line(context, "}"); } @@ -523,7 +540,10 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater { cgltf_write_line(context, "{"); cgltf_write_strprop(context, "name", material->name); - cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f); + if (material->alpha_mode == cgltf_alpha_mode_mask) + { + cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f); + } cgltf_write_boolprop_optional(context, "doubleSided", material->double_sided, false); // cgltf_write_boolprop_optional(context, "unlit", material->unlit, false); @@ -662,7 +682,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture); if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f)) { - cgltf_write_floatarrayprop(context, "dffuseFactor", params->diffuse_factor, 4); + cgltf_write_floatarrayprop(context, "diffuseFactor", params->diffuse_factor, 4); } if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f)) { @@ -707,6 +727,18 @@ static void cgltf_write_texture(cgltf_write_context* context, const cgltf_textur cgltf_write_strprop(context, "name", texture->name); CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images); CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers); + + if (texture->has_basisu) + { + cgltf_write_line(context, "\"extensions\": {"); + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_BASISU; + cgltf_write_line(context, "\"KHR_texture_basisu\": {"); + CGLTF_WRITE_IDXPROP("source", texture->basisu_image, context->data->images); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } cgltf_write_extras(context, &texture->extras); cgltf_write_line(context, "}"); } @@ -894,7 +926,7 @@ static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_acces cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type)); cgltf_size dim = cgltf_dim_from_type(accessor->type); cgltf_write_boolprop_optional(context, "normalized", accessor->normalized, false); - cgltf_write_intprop(context, "byteOffset", (int)accessor->offset, 0); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->offset, 0); cgltf_write_intprop(context, "count", (int)accessor->count, -1); if (accessor->has_min) { @@ -909,13 +941,13 @@ static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_acces cgltf_write_line(context, "\"sparse\": {"); cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0); cgltf_write_line(context, "\"indices\": {"); - cgltf_write_intprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0); CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views); cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0); cgltf_write_extras(context, &accessor->sparse.indices_extras); cgltf_write_line(context, "}"); cgltf_write_line(context, "\"values\": {"); - cgltf_write_intprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0); CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views); cgltf_write_extras(context, &accessor->sparse.values_extras); cgltf_write_line(context, "}"); @@ -1059,6 +1091,12 @@ static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extens if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS) { cgltf_write_stritem(context, "KHR_materials_variants"); } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME) { + cgltf_write_stritem(context, "KHR_materials_volume"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) { + cgltf_write_stritem(context, "KHR_texture_basisu"); + } } cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data) @@ -1273,7 +1311,7 @@ cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size si /* cgltf is distributed under MIT license: * - * Copyright (c) 2019 Philip Rideout + * Copyright (c) 2019-2021 Philip Rideout * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/third_party/cgltf/tnt/README b/third_party/cgltf/tnt/README index ef404e08f7..db44fc4d9d 100644 --- a/third_party/cgltf/tnt/README +++ b/third_party/cgltf/tnt/README @@ -1,6 +1,6 @@ This folder was last updated as follows: - export sha=1bdc84d + export sha=aef41ea cd third_party curl -L -O https://github.com/jkuhlmann/cgltf/archive/${sha}.zip unzip ${sha}.zip diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 0b4925d0b5..3701f698a2 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.12.5", + "version": "1.12.6", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",