diff --git a/README.md b/README.md index 43a3ef8de2b..4322afea615 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.54.3' + implementation 'com.google.android.filament:filament-android:1.54.4' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.54.3' +pod 'Filament', '~> 1.54.4' ``` ## Documentation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 48144bb1d81..c3bede1e3f5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.54.4 + +- Add support for multi-layered render target with array textures. + ## v1.54.3 diff --git a/android/gradle.properties b/android/gradle.properties index 6b5d7c8454c..e0c7ac6c119 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.54.3 +VERSION_NAME=1.54.4 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index 69730d688c8..d12ac923bb5 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -20,11 +20,16 @@ #include #endif +#include #include +#include #include #include +#include #include +#include +#include #ifdef __ANDROID__ #include @@ -74,8 +79,8 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept } void CommandStream::execute(void* buffer) { - SYSTRACE_CALL(); - SYSTRACE_CONTEXT(); + // NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also + // uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope. Profiler profiler; @@ -100,6 +105,7 @@ void CommandStream::execute(void* buffer) { // we want to remove all this when tracing is completely disabled profiler.stop(); UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters(); + SYSTRACE_CONTEXT(); SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions()); SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles()); SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10); diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp index 67cc54e5914..79ae8ed72ab 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp @@ -32,8 +32,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout( } // build the push constant layout key - uint32_t pushConstantRangeCount = program->getPushConstantRangeCount(); - auto const& pushConstantRanges = program->getPushConstantRanges(); + uint32_t const pushConstantRangeCount = program->getPushConstantRangeCount(); + auto const& pushConstantRanges = program->getPushConstantRanges(); if (pushConstantRangeCount > 0) { assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT); for (uint8_t i = 0; i < pushConstantRangeCount; ++i) { @@ -52,8 +52,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout( } } - if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) { - PipelineLayoutCacheEntry& entry = iter.value(); + if (auto iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) { + PipelineLayoutCacheEntry& entry = iter->second; entry.lastUsed = mTimestamp++; return entry.handle; } diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h index 42bd2926acd..25c401b21e7 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h @@ -22,7 +22,7 @@ #include -#include +#include namespace filament::backend { @@ -36,8 +36,8 @@ class VulkanPipelineLayoutCache { void terminate() noexcept; struct PushConstantKey { - uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc). - uint8_t size; + uint8_t stage = 0;// We have one set of push constant per shader stage (fragment, vertex, etc). + uint8_t size = 0; // Note that there is also an offset parameter for push constants, but // we always assume our update range will have the offset 0. }; @@ -73,7 +73,7 @@ class VulkanPipelineLayoutCache { } }; - using PipelineLayoutMap = tsl::robin_map; VkDevice mDevice; @@ -82,6 +82,6 @@ class VulkanPipelineLayoutCache { PipelineLayoutMap mPipelineLayouts; }; -} +} // filament::backend #endif // TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index d4c9848de8f..139dcac7f40 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -570,6 +570,13 @@ class UTILS_PUBLIC View : public FilamentAPI { */ void setShadowType(ShadowType shadow) noexcept; + /** + * Returns the shadow mapping technique used by this View. + * + * @return value set by setShadowType(). + */ + ShadowType getShadowType() const noexcept; + /** * Sets VSM shadowing options that apply across the entire View. * diff --git a/filament/src/FrameSkipper.cpp b/filament/src/FrameSkipper.cpp index ef14f5a0f82..673e82ef4f8 100644 --- a/filament/src/FrameSkipper.cpp +++ b/filament/src/FrameSkipper.cpp @@ -31,8 +31,7 @@ using namespace utils; using namespace backend; FrameSkipper::FrameSkipper(size_t latency) noexcept - : mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) { - assert_invariant(latency <= MAX_FRAME_LATENCY); + : mLast(std::clamp(latency, size_t(1), MAX_FRAME_LATENCY) - 1) { } FrameSkipper::~FrameSkipper() noexcept = default; diff --git a/filament/src/FrameSkipper.h b/filament/src/FrameSkipper.h index 61bc4049735..814299341a2 100644 --- a/filament/src/FrameSkipper.h +++ b/filament/src/FrameSkipper.h @@ -32,7 +32,18 @@ namespace filament { * outrun the GPU. */ class FrameSkipper { - static constexpr size_t MAX_FRAME_LATENCY = 3; + /* + * The maximum frame latency acceptable on ANDROID is 2 because higher latencies will be + * throttled anyway in BufferQueueProducer::dequeueBuffer(), because ANDROID is generally + * triple-buffered no more; that case is actually pretty bad because the GL thread can block + * anywhere (usually inside the first draw command that touches the swapchain). + * + * A frame latency of 1 has the benefit of reducing render latency, + * but the drawback of preventing CPU / GPU overlap. + * + * Generally a frame latency of 2 is the best compromise. + */ + static constexpr size_t MAX_FRAME_LATENCY = 2; public: /* * The latency parameter defines how many unfinished frames we want to accept before we start diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 65635f3f647..0d5d3aa61ca 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -193,6 +193,10 @@ void View::setShadowType(View::ShadowType shadow) noexcept { downcast(this)->setShadowType(shadow); } +View::ShadowType View::getShadowType() const noexcept { + return downcast(this)->getShadowType(); +} + void View::setVsmShadowOptions(VsmShadowOptions const& options) noexcept { downcast(this)->setVsmShadowOptions(options); } diff --git a/filament/src/details/RenderTarget.cpp b/filament/src/details/RenderTarget.cpp index dad0af4d633..421df0dcc34 100644 --- a/filament/src/details/RenderTarget.cpp +++ b/filament/src/details/RenderTarget.cpp @@ -34,7 +34,7 @@ struct RenderTarget::BuilderDetails { uint32_t mWidth{}; uint32_t mHeight{}; uint8_t mSamples = 1; // currently not settable in the public facing API - uint8_t mLayerCount = 0;// currently not settable in the public facing API + uint8_t mLayerCount = 1; }; using BuilderType = RenderTarget; @@ -91,22 +91,28 @@ RenderTarget* RenderTarget::Builder::build(Engine& engine) { uint32_t maxWidth = 0; uint32_t minHeight = std::numeric_limits::max(); uint32_t maxHeight = 0; + uint32_t minDepth = std::numeric_limits::max(); + uint32_t maxDepth = 0; for (auto const& attachment : mImpl->mAttachments) { if (attachment.texture) { const uint32_t w = attachment.texture->getWidth(attachment.mipLevel); const uint32_t h = attachment.texture->getHeight(attachment.mipLevel); + const uint32_t d = attachment.texture->getDepth(attachment.mipLevel); minWidth = std::min(minWidth, w); minHeight = std::min(minHeight, h); + minDepth = std::min(minDepth, d); maxWidth = std::max(maxWidth, w); maxHeight = std::max(maxHeight, h); + maxDepth = std::max(maxDepth, d); } } - FILAMENT_CHECK_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight) - << "All attachments dimensions must match"; + FILAMENT_CHECK_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight + && minDepth == maxDepth) << "All attachments dimensions must match"; mImpl->mWidth = minWidth; mImpl->mHeight = minHeight; + mImpl->mLayerCount = minDepth; return downcast(engine).createRenderTarget(*this); } diff --git a/filament/src/fg/FrameGraph.cpp b/filament/src/fg/FrameGraph.cpp index fec8580326a..d7c2a6f07eb 100644 --- a/filament/src/fg/FrameGraph.cpp +++ b/filament/src/fg/FrameGraph.cpp @@ -195,12 +195,11 @@ FrameGraph& FrameGraph::compile() noexcept { void FrameGraph::execute(backend::DriverApi& driver) noexcept { - SYSTRACE_CALL(); - bool const useProtectedMemory = mMode == Mode::PROTECTED; auto const& passNodes = mPassNodes; auto& resourceAllocator = mResourceAllocator; + SYSTRACE_NAME("FrameGraph"); driver.pushGroupMarker("FrameGraph"); auto first = passNodes.begin(); @@ -211,7 +210,6 @@ void FrameGraph::execute(backend::DriverApi& driver) noexcept { assert_invariant(!node->isCulled()); SYSTRACE_NAME(node->getName()); - driver.pushGroupMarker(node->getName()); // devirtualize resourcesList @@ -229,7 +227,6 @@ void FrameGraph::execute(backend::DriverApi& driver) noexcept { assert_invariant(resource->last == node); resource->destroy(resourceAllocator); } - driver.popGroupMarker(); } driver.popGroupMarker(); diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index c324c7cc115..07e8121b8e1 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.54.3" + spec.version = "1.54.4" 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.54.3/filament-v1.54.3-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.4/filament-v1.54.4-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/utils/include/utils/memalign.h b/libs/utils/include/utils/memalign.h index 4c048bffdaf..c8c08fe1dea 100644 --- a/libs/utils/include/utils/memalign.h +++ b/libs/utils/include/utils/memalign.h @@ -73,8 +73,8 @@ class STLAlignedAllocator { using const_pointer = const TYPE*; using reference = TYPE&; using const_reference = const TYPE&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + using size_type = ::size_t; + using difference_type = ::ptrdiff_t; using propagate_on_container_move_assignment = std::true_type; using is_always_equal = std::true_type; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 7cd8ce6b9e6..0ff03049a7e 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -22,6 +22,7 @@ set(MATERIAL_SRCS materials/bakedTexture.mat materials/pointSprites.mat materials/aoPreview.mat + materials/arrayTexture.mat materials/groundShadow.mat materials/heightfield.mat materials/image.mat @@ -252,6 +253,7 @@ if (NOT ANDROID) add_demo(helloskinning) add_demo(helloskinningbuffer) add_demo(helloskinningbuffer_morebones) + add_demo(hellostereo) add_demo(image_viewer) add_demo(lightbulb) add_demo(material_sandbox) @@ -274,6 +276,7 @@ if (NOT ANDROID) target_link_libraries(gltf_viewer PRIVATE gltf-demo-resources uberarchive gltfio viewer) target_link_libraries(gltf_instances PRIVATE gltf-demo-resources uberarchive gltfio viewer) target_link_libraries(hellopbr PRIVATE filameshio suzanne-resources) + target_link_libraries(hellostereo PRIVATE filameshio suzanne-resources) target_link_libraries(image_viewer PRIVATE viewer imageio) target_link_libraries(multiple_windows PRIVATE filameshio suzanne-resources) target_link_libraries(rendertarget PRIVATE filameshio suzanne-resources) diff --git a/samples/hellostereo.cpp b/samples/hellostereo.cpp new file mode 100644 index 00000000000..fff1616ace0 --- /dev/null +++ b/samples/hellostereo.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2024 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include + +#include +#include + +#include "generated/resources/resources.h" +#include "generated/resources/monkey.h" + +using namespace filament; +using namespace filamesh; +using namespace filament::math; + +struct Vertex { + float3 position; + float2 uv; +}; + +struct App { + Config config; + + Material* monkeyMaterial; + MaterialInstance* monkeyMatInstance; + MeshReader::Mesh monkeyMesh; + mat4f monkeyTransform; + utils::Entity lightEntity; + + View* stereoView = nullptr; + Scene* stereoScene = nullptr; + Camera* stereoCamera = nullptr; + Texture* stereoColorTexture = nullptr; + Texture* stereoDepthTexture = nullptr; + RenderTarget* stereoRenderTarget = nullptr; + + VertexBuffer* quadVb = nullptr; + IndexBuffer* quadIb = nullptr; + Material* quadMaterial = nullptr; + std::vector quadEntities; + std::vector quadMatInstances; +}; + +static void printUsage(char* name) { + std::string exec_name(utils::Path(name).getName()); + std::string usage( + "SHOWCASE renders multiple quads displaying the contents of stereoscopic rendering\n" + "Usage:\n" + " SHOWCASE [options]\n" + "Options:\n" + " --help, -h\n" + " Prints this message\n\n" + " --api, -a\n" + " Specify the backend API: opengl (default), vulkan, or metal\n" + " --eyes=, -y \n" + " Sets the number of stereoscopic eyes (default: 2) when stereoscopic rendering is\n" + " enabled.\n\n" + ); + const std::string from("SHOWCASE"); + for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) { + usage.replace(pos, from.length(), exec_name); + } + std::cout << usage; +} + +static int handleCommandLineArguments(int argc, char* argv[], App* app) { + static constexpr const char* OPTSTR = "ha:y:"; + static const struct option OPTIONS[] = { + { "help", no_argument, nullptr, 'h' }, + { "api", required_argument, nullptr, 'a' }, + { "eyes", required_argument, nullptr, 'y' }, + { nullptr, 0, nullptr, 0 } + }; + int opt; + int option_index = 0; + while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) { + std::string arg(optarg ? optarg : ""); + switch (opt) { + default: + case 'h': + printUsage(argv[0]); + exit(0); + case 'a': + if (arg == "opengl") { + app->config.backend = Engine::Backend::OPENGL; + } else if (arg == "vulkan") { + app->config.backend = Engine::Backend::VULKAN; + } else if (arg == "metal") { + app->config.backend = Engine::Backend::METAL; + } else { + std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n"; + exit(1); + } + break; + case 'y': { + int eyeCount = 0; + try { + eyeCount = std::stoi(arg); + } catch (std::invalid_argument &e) { } + if (eyeCount >= 2 && eyeCount <= CONFIG_MAX_STEREOSCOPIC_EYES) { + app->config.stereoscopicEyeCount = eyeCount; + } else { + std::cerr << "Eye count must be between 2 and CONFIG_MAX_STEREOSCOPIC_EYES (" + << (int)CONFIG_MAX_STEREOSCOPIC_EYES << ") (inclusive).\n"; + exit(1); + } + break; + } + } + } + return optind; +} + +int main(int argc, char** argv) { + +#if !defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) + std::cerr << "This sample only works with multiview enabled.\n"; + exit(1); +#endif + + App app{}; + app.config.title = "stereoscopic rendering"; + handleCommandLineArguments(argc, argv, &app); + + auto setup = [&app](Engine* engine, View* view, Scene* scene) { + auto& tcm = engine->getTransformManager(); + auto& rcm = engine->getRenderableManager(); + auto& em = utils::EntityManager::get(); + auto vp = view->getViewport(); + + constexpr float3 monkeyPosition{ 0, 0, -4}; + constexpr float3 upVector{ 0, 1, 0}; + const int eyeCount = app.config.stereoscopicEyeCount; + + // Create a mesh material and an instance. + app.monkeyMaterial = Material::Builder() + .package(RESOURCES_AIDEFAULTMAT_DATA, RESOURCES_AIDEFAULTMAT_SIZE) + .build(*engine); + auto mi = app.monkeyMatInstance = app.monkeyMaterial->createInstance(); + mi->setParameter("baseColor", RgbType::LINEAR, {0.8, 1.0, 1.0}); + mi->setParameter("metallic", 0.0f); + mi->setParameter("roughness", 0.4f); + mi->setParameter("reflectance", 0.5f); + + // Add a monkey and a light source into the main scene. + app.monkeyMesh = MeshReader::loadMeshFromBuffer( + engine, MONKEY_SUZANNE_DATA, nullptr, nullptr, mi); + auto ti = tcm.getInstance(app.monkeyMesh.renderable); + app.monkeyTransform = mat4f{mat3f(1), monkeyPosition } * tcm.getWorldTransform(ti); + rcm.setCastShadows(rcm.getInstance(app.monkeyMesh.renderable), false); + scene->addEntity(app.monkeyMesh.renderable); + + app.lightEntity = em.create(); + LightManager::Builder(LightManager::Type::SUN) + .color(Color::toLinear(sRGBColor(0.98f, 0.92f, 0.89f))) + .intensity(110000) + .direction({ 0.7, -1, -0.8 }) + .sunAngularRadius(1.9f) + .castShadows(false) + .build(*engine, app.lightEntity); + scene->addEntity(app.lightEntity); + + // Create a stereo render target that will be rendered as an offscreen view. + app.stereoScene = engine->createScene(); + app.stereoScene->addEntity(app.monkeyMesh.renderable); + app.stereoScene->addEntity(app.lightEntity); + app.stereoView = engine->createView(); + app.stereoView->setScene(app.stereoScene); + app.stereoView->setPostProcessingEnabled(false); + app.stereoColorTexture = Texture::Builder() + .width(vp.width) + .height(vp.height) + .depth(eyeCount) + .levels(1) + .sampler(Texture::Sampler::SAMPLER_2D_ARRAY) + .format(Texture::InternalFormat::RGBA8) + .usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE) + .build(*engine); + app.stereoDepthTexture = Texture::Builder() + .width(vp.width) + .height(vp.height) + .depth(eyeCount) + .levels(1) + .sampler(Texture::Sampler::SAMPLER_2D_ARRAY) + .format(Texture::InternalFormat::DEPTH24) + .usage(Texture::Usage::DEPTH_ATTACHMENT) + .build(*engine); + app.stereoRenderTarget = RenderTarget::Builder() + .texture(RenderTarget::AttachmentPoint::COLOR, app.stereoColorTexture) + .texture(RenderTarget::AttachmentPoint::DEPTH, app.stereoDepthTexture) + .build(*engine); + app.stereoView->setRenderTarget(app.stereoRenderTarget); + app.stereoView->setViewport({0, 0, vp.width, vp.height}); + app.stereoCamera = engine->createCamera(em.create()); + app.stereoView->setCamera(app.stereoCamera); + app.stereoView->setStereoscopicOptions({.enabled = true}); + FilamentApp::get().addOffscreenView(app.stereoView); + + // Camera settings for the stereo render target + constexpr double projNear = 0.1; + constexpr double projFar = 100; + + mat4 projections[CONFIG_MAX_STEREOSCOPIC_EYES]; + mat4 eyeModels[CONFIG_MAX_STEREOSCOPIC_EYES]; + static_assert(CONFIG_MAX_STEREOSCOPIC_EYES == 4, "Update matrices"); + projections[0] = Camera::projection(24, 1.0, projNear, projFar); + projections[1] = Camera::projection(70, 1.0, projNear, projFar); + projections[2] = Camera::projection(50, 1.0, projNear, projFar); + projections[3] = Camera::projection(35, 1.0, projNear, projFar); + app.stereoCamera->setCustomEyeProjection(projections, 4, projections[0], projNear, projFar); + + eyeModels[0] = mat4::lookAt(float3{ -4, 0, 0 }, monkeyPosition, upVector); + eyeModels[1] = mat4::lookAt(float3{ 4, 0, 0 }, monkeyPosition, upVector); + eyeModels[2] = mat4::lookAt(float3{ 0, 3, 0 }, monkeyPosition, upVector); + eyeModels[3] = mat4::lookAt(float3{ 0, -3, 0 }, monkeyPosition, upVector); + for (int i = 0; i < eyeCount; ++i) { + app.stereoCamera->setEyeModelMatrix(i, eyeModels[i]); + } + + // Create a vertex buffer and an index buffer for a quad. This will be used to display the contents + // of each layer of the stereo texture. + float3 quadCenter = {0, 0, 0}; + float3 quadNormal = normalize(float3 {0, 0, 1}); + float3 u = normalize(cross(quadNormal, upVector)); + float3 v = cross(quadNormal, u); + static Vertex quadVertices[4] = { + {{quadCenter - u - v}, {1, 0}}, + {{quadCenter + u - v}, {0, 0}}, + {{quadCenter - u + v}, {1, 1}}, + {{quadCenter + u + v}, {0, 1}} + }; + + static_assert(sizeof(Vertex) == 20, "Strange vertex size."); + app.quadVb = VertexBuffer::Builder() + .vertexCount(4) + .bufferCount(1) + .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(Vertex)) + .attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 12, sizeof(Vertex)) + .build(*engine); + app.quadVb->setBufferAt(*engine, 0, + VertexBuffer::BufferDescriptor(quadVertices, sizeof(Vertex) * 4, nullptr)); + + static constexpr uint16_t quadIndices[6] = { 0, 1, 2, 3, 2, 1 }; + app.quadIb = IndexBuffer::Builder() + .indexCount(6) + .bufferType(IndexBuffer::IndexType::USHORT) + .build(*engine); + app.quadIb->setBuffer(*engine, IndexBuffer::BufferDescriptor(quadIndices, 12, nullptr)); + + // Create quad material instances and renderables. + app.quadMaterial = Material::Builder() + .package(RESOURCES_ARRAYTEXTURE_DATA, RESOURCES_ARRAYTEXTURE_SIZE) + .build(*engine); + + for (int i = 0; i < eyeCount; ++i) { + MaterialInstance* quadMatInst = app.quadMaterial->createInstance(); + TextureSampler sampler(TextureSampler::MinFilter::LINEAR, TextureSampler::MagFilter::LINEAR); + quadMatInst->setParameter("image", app.stereoColorTexture, sampler); + quadMatInst->setParameter("layerIndex", i); + quadMatInst->setParameter("borderEffect", true); + app.quadMatInstances.push_back(quadMatInst); + + utils::Entity quadEntity = em.create(); + app.quadEntities.push_back(quadEntity); + RenderableManager::Builder(1) + .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }}) + .material(0, quadMatInst) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.quadVb, app.quadIb, 0, 6) + .culling(false) + .receiveShadows(false) + .castShadows(false) + .build(*engine, quadEntity); + scene->addEntity(quadEntity); + + // Place quads at equal intervals. + TransformManager::Instance quadTi = tcm.getInstance(quadEntity); + mat4f quadWorld = tcm.getWorldTransform(quadTi); + constexpr float leftMostPos = -4; + constexpr float rightMostPos = 4; + float xpos = leftMostPos + ( (rightMostPos - leftMostPos) / (eyeCount - 1) ) * i; + tcm.setTransform(quadTi, mat4f::translation(float3(xpos, 2, -8)) * quadWorld); + } + }; + + auto cleanup = [&app](Engine* engine, View*, Scene*) { + + auto& em = utils::EntityManager::get(); + + for (MaterialInstance* mi : app.quadMatInstances) { + engine->destroy(mi); + } + for (utils::Entity e : app.quadEntities) { + engine->destroy(e); + } + engine->destroy(app.quadMaterial); + engine->destroy(app.quadIb); + engine->destroy(app.quadVb); + engine->destroy(app.stereoRenderTarget); + engine->destroy(app.stereoDepthTexture); + engine->destroy(app.stereoColorTexture); + auto camera = app.stereoCamera->getEntity(); + engine->destroyCameraComponent(camera); + em.destroy(camera); + engine->destroy(app.stereoScene); + engine->destroy(app.stereoView); + engine->destroy(app.lightEntity); + engine->destroy(app.monkeyMesh.renderable); + engine->destroy(app.monkeyMesh.indexBuffer); + engine->destroy(app.monkeyMesh.vertexBuffer); + engine->destroy(app.monkeyMatInstance); + engine->destroy(app.monkeyMaterial); + }; + + auto preRender = [&app](Engine*, View*, Scene*, Renderer* renderer) { + renderer->setClearOptions({.clearColor = {0.1,0.2,0.4,1.0}, .clear = true}); + }; + + FilamentApp::get().animate([&app](Engine* engine, View* view, double now) { + auto& tcm = engine->getTransformManager(); + + // Animate the monkey by spinning and sliding back and forth along Z. + auto ti = tcm.getInstance(app.monkeyMesh.renderable); + mat4f xform = app.monkeyTransform * mat4f::rotation(now, float3{0, 1, 0 }); + tcm.setTransform(ti, xform); + }); + + FilamentApp::get().run(app.config, setup, cleanup, FilamentApp::ImGuiCallback(), preRender); + + return 0; +} diff --git a/samples/materials/arrayTexture.mat b/samples/materials/arrayTexture.mat new file mode 100644 index 00000000000..f14a86bc864 --- /dev/null +++ b/samples/materials/arrayTexture.mat @@ -0,0 +1,39 @@ +material { + name : ArrayTexture, + parameters : [ + { + type : sampler2dArray, + name : image + }, + { + type : int, + name : layerIndex + }, + { + type : bool, + name : borderEffect + } + ], + requires : [ + uv0 + ], + shadingModel : unlit, + culling : none +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + float3 v = texture(materialParams_image, vec3(getUV0(), materialParams.layerIndex)).rgb; + material.baseColor.rgb = v; + + // Add black border effect. + if (materialParams.borderEffect) { + vec2 st = getUV0(); + float minDist0 = min(st.x, st.y); + float minDist1 = min(1.0 - st.x, 1.0 - st.y); + float minDist = min(minDist0, minDist1); + material.baseColor.rgb *= smoothstep(0.0, 0.1, minDist); + } + } +} diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 410de0fec0a..eccad539620 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.54.3", + "version": "1.54.4", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",