Compare commits

..
Author SHA1 Message Date
Benjamin Doherty e2fa38590d Update android build.gradle publishing instructions 2023-03-15 12:54:02 -07:00
Benjamin Doherty 2a579c460f Merge branch 'rc/1.32.0' into release 2023-03-14 10:46:27 -07:00
Ben Doherty efdc801cff Vulkan: fix stack use-after-free (#6639) 2023-03-14 10:24:18 -07:00
Benjamin Doherty dd654c575a Bump MATERIAL_VERSION to 32 2023-03-13 14:04:13 -07:00
Benjamin Doherty 0c2599b6ca Bump version to 1.32.0 2023-03-09 10:40:39 -08:00
Benjamin Doherty 979d6742e0 Merge branch 'rc/1.31.7' into release 2023-03-09 10:39:41 -08:00
Benjamin Doherty 7cca872af5 Release Filament 1.31.7 2023-03-09 10:39:17 -08:00
Mathias Agopian 3cef1991b2 Minor cleanup of GL backend 2023-03-08 12:49:22 -08:00
Mathias Agopian 4a7a033d04 Fix timer query breakage on webgl 2023-03-08 12:33:11 -08:00
Mathias Agopian 64493d8636 Fix timer query breakage on webgl 2023-03-08 12:06:59 -08:00
Powei Feng df78322bdc android: fix sample-material-builder app name (#6624)
fixes #6622
2023-03-08 11:11:56 -08:00
Ben Doherty 5c2bbcb4a1 Remove compute shader assertion in GLProgram 2023-03-08 10:56:24 -08:00
Ben Doherty f1a967d631 Update web docs and include ktx2 assets (#6616) 2023-03-08 10:55:13 -08:00
Ben Doherty ec48dfa453 Remove compute shader assertion in GLProgram 2023-03-07 23:31:33 -08:00
Deadlock Yoon 5881a13b20 Metal: fix memory leak in readPixels
The PixelBufferDescriptor was not being deallocated properly, which
resulted in the leak. This patch explicitly deletes the
PixelBufferDescriptor at the end of the callback to prevent the leak
.This is necessary as the move constructor does not automatically
deallocate the existing PixelBufferDescriptor.
2023-03-07 11:02:39 -08:00
Mathias Agopian efd9e875db fix a discard flags issue
An attachment would be wrongly discarded if used as read-only: because
it didn't have a dependency to it, the discard flag was set.
This is fixed by setting the discard flag only if a resource is written
and has dependencies to it.

Should fix: #5005
2023-03-06 22:39:11 -08:00
Powei Feng 6f25e8ae5a vulkan: async readPixels (#6605)
- Add a TaskHandler class for process events on the backend
   thread
 - Check fence status when handler runs and copy bits to the client
   when the fence is reached.
2023-03-06 18:03:28 -08:00
Mathias Agopian 449519c1b4 cleanup fog
- update comments and some names
2023-03-06 16:28:31 -08:00
Mathias Agopian e2e5f7cee9 Add way to retrieve the user world-space in materials (#6607)
* Add way to retrieve the user world-space in materials

added `getUserWorldFromWorldMatrix()` and `getUserWorldPosition()` to
retrieve the API-level (user) world position in materials.
Deprecated `getWorldOffset()`

`getWorldOffset` didn't work when an IBL rotation was applied.

* fix large scenes with an ibl rotation

Rotate the IBL around the camera instead of the world so that the camera 
is always at the origin regardless of the rotation.
2023-03-03 21:34:17 -08:00
Powei Feng 9d30233a38 geometry: clean up TangentSpaceMesh (#6595)
- Reordered the 'const var' declarations to 'var const'
 - Use std::vector for data arrays instead of manually managing
   allocations
2023-03-03 15:33:28 -08:00
Ben Doherty d684fc4096 Compute shading_view will full precision (#6611) 2023-03-03 13:48:36 -08:00
Ben Doherty 5f3f3f1b24 Fix NaN parameters in material_sandbox (#6609) 2023-03-03 12:13:06 -08:00
Romain Guy 6aa2a2b89b Update getopt to musl-1.1's implementation (#6610)
* Update getopt to musl-1.1's implementation

Our former implementation was rather ancient and was using unsupported
features of C.

* Fix Windows
2023-03-03 11:56:32 -08:00
Powei Feng 47135164ee vulkan: fix validation BestPractices-vkCmdBeginRenderPass-ClearValueWithoutLoadOpClear (#6608) 2023-03-03 11:14:05 -08:00
Romain Guy 5a75fb9b53 Add new alphaToCoverage material property (#6606)
* Add new alphaToCoverage material property

The alphaToCoverage property lets you enable or disable alpha to coverage
in a material. More importantly it lets you overrides the behavior of
blending: masked which automatically enables alphaToCoverage.

* Update release notes
2023-03-02 12:40:55 -08:00
Mathias Agopian 7933b08e85 fix fog instability with strong falloff
The fog calculation could fail when the falloff was strong and the
camera z position was far from the fog height. The problem was
that the computation was spread between the cpu and gpu by splitting
an exp(), but with certain parameters the two exp() would independently
blow-up.  We fix this by doing the exp() only on the gpu side.
2023-03-01 16:48:16 -08:00
Mathias Agopian a6809b4550 Fix fog's z origin
the fog's z origin was always the camera height instead of being the
ground (or more precisely the value of fogHeight).
2023-03-01 16:48:16 -08:00
Mathias Agopian 8f77308bca fix fog integral computation
1-a/b not the same as (1-a)/b !
2023-03-01 16:48:16 -08:00
Mathias Agopian 65f1e64812 fix fog heigh falloff computation
it was especially wrong when near or at 0.
2023-03-01 16:48:16 -08:00
Mathias Agopian 79942b3bea fog parameters should be calculated in highp 2023-03-01 16:48:16 -08:00
Mathias Agopian 9ee41b9b6a rework again how we manage different versions of GL
try to be more explicit about which configurations are supported,
and use the same pattern everywhere for checking the gl version at
either compile and runtime.
2023-03-01 15:59:04 -08:00
Mathias Agopian 21995e4ac9 clip_control could fail on desktop
This didn't happen in practice, but we would call (null) if the
clip_control extension was available and we were not on GL 4.1.

We "fix" this by not supporting the clip_control extension on desktop.
It doesn't matter because clip_control is core as of 4.5 and is 
present in only 8% of non 4.5 GL implementation, and this doesn't
include any macOS versions.
2023-03-01 15:59:04 -08:00
Mathias Agopian 8dc871c86e remove code for unused extensions 2023-03-01 15:59:04 -08:00
Benjamin Doherty dec903a0ee Bump version to 1.31.7 2023-03-01 11:26:50 -08:00
Benjamin Doherty e9475b322b Merge branch 'rc/1.31.6' into release 2023-03-01 11:24:13 -08:00
Benjamin Doherty 78785a5bca Release Filament 1.31.6 2023-03-01 11:23:37 -08:00
Mathias Agopian 662b4b5ca3 fix typo that could cause a OOB
fortunately this would only happen when using SSBOs (which are not
used currently).
2023-02-27 13:06:25 -08:00
Mathias Agopian bee96d1cf1 abstract OpenGL's GLSync
This small abstraction is (will be) needed because GLES 2.0 doesn't
have sync object, however synchronization is sometimes available
externally, in particular with EGL.

This PR doesn't provide other implementations.
2023-02-27 12:40:21 -08:00
Mathias Agopian 122ebe1ccb More improvements to how we handle extensions (#6591)
- make sure to initialize all extension booleans, we treat them
  as feature flags.
- be more explicit about #define'ing gl tokens, so we can more easily
  catch errors later.
- don't blindly use extension tokens that might not be available
  (e.g.: GL_TEXTURE_EXTERNAL_OES or GL_TEXTURE_CUBE_MAP_ARRAY). It
  would probably cause a spurious gl error.
2023-02-24 16:52:43 -08:00
Mathias Agopian e741e165ae remove unused / no longer used code 2023-02-24 16:50:15 -08:00
Mathias Agopian d38dc24915 Cleanup how we get GL extensions entry-points
- iOS is treated the same way than Android now. The only difference
  is that iOS only provides prototypes and no typedef, whereas 
  Android only provides typedefs and no prototypes.

- Timer queries is core in GL and an extension on all versions of
  GLES. On iOS it's not available at the header level. An additional
  subtlety is that glGetObjectuiv is core in GLES 3.0, so it conflicts
  with the extension.
  So, now we do things correctly:
  - on desktop we use the core methods
  - on ios we ifdef out everything related to timer queries
  - on gles 2.0 and up we use only the extension entry-points
2023-02-24 11:55:22 -08:00
Powei Feng d12612f091 Add UV-based tangent space algorithms (#6572)
Added mikktspace as a third party lib
2023-02-24 09:32:02 -08:00
Mathias Agopian e6d73135c1 fix DEPTH_STENCIL case for fbo attachments
We were not testing for that case properly. This case is taken when 
either:
- depth & stencil textures are the same and not null
- or, only depth is specified but both attachments are requested


Also cleanup the dimension checks in debug builds.
2023-02-23 14:47:17 -08:00
Mathias Agopian 816612cf0a Fix readPixels() for "auto-resolved" MS color buffers
glReadPixel doesn't resolve automatically, but it does with the 
auto-resolve extension, which we're always emulating.
2023-02-23 10:56:08 -08:00
Mathias Agopian 7ca8ba272c don't rely on GL PACK/UNPACK convenience
- some of the convenience are not available in ES2
- it's less efficient
- we can save some PBO space when reading back
  a partial framebuffer.

We also avoid using GL_PACK_ROW_LENGTH which technically is not
a convenience, but in our case we are doing an extra copy anyways, so
we can account for the row-length at that point.
2023-02-23 10:55:46 -08:00
Mathias Agopian d337a6d019 Scene::prepare is now multithreaded 2023-02-22 16:28:40 -08:00
Mathias Agopian f79c4c6080 Improve directional shadow performance with large scenes
- We now call visitScene only once for the directional shadowmap instead of
1 + cascade_count times.

- Don't use visitScene for spot shadows

it was only used to compute the near/far planes, but instead we can
use the radius of the light. This could degrade the quality of the
spot shadows, but this can be corrected by setting a correct radius.

caveat: currently the near is hardcoded to 0.01 units. this should be
user-settable.

- Improve performance of visitScene calls

instead of transforming 8 points of an AABB and finding the min/max,
we transform the AABB and use its minz and maxz. This works for affine
transforms.

This change also cleans-up AABB and Box transform APIs, which also
are now inline.
2023-02-22 16:28:40 -08:00
Mathias Agopian 2e7fc6229b Improve StructureOfArray
- StructureOfArray: don't initialize trivial ctors

We mimic the behavior of std::vector<> here, where a resize() won't
initialize the array if the type is trivially_default_constructible.
This can reveal existing bugs, where we depended on the initialization
to 0.

- StructureOfArray: add push_back(std::tuple<>)

This basically allows us to push_back() a struct of the SoA.

- Make PerRenderableData trivially constructible

this improves performance when we have tons of objects in the scene
because PerRenderableData is used in arrays.
2023-02-22 16:28:40 -08:00
Mathias Agopian fc724575fa Make SYSTRACE_CALL work with SYSTRACE_CONTEXT
SYSTRACE_VALUE now requires a SYSTRACE_CONTEXT.
2023-02-22 16:28:40 -08:00
Mathias Agopian 84716610b8 cleanup: make const everything that can be 2023-02-22 16:28:40 -08:00
Benjamin Doherty d9aead8aac Linux CI: use clang 14 2023-02-21 16:06:48 -08:00
Powei Feng ce148ebeb1 Update linux clang to version 10 (#6556) 2023-02-21 16:00:07 -08:00
Powei Feng e1315fbaa7 Update CI Ubuntu version 2023-02-21 15:57:00 -08:00
Benjamin Doherty 1690549392 Bump version to 1.31.6 2023-02-21 13:18:22 -08:00
Benjamin Doherty c73710e343 Merge branch 'rc/1.31.5' into release 2023-02-21 13:16:47 -08:00
Benjamin Doherty 29983772ed Release Filament 1.31.5 2023-02-21 13:16:09 -08:00
ViktorHeisenberger d100e628c2 Invoke ccache on Win+ninja. There is no support for ENV vars. (#6582) 2023-02-21 09:19:03 -08:00
Mathias Agopian 94f121f37d Add a camera near/far setting in gltf_viewer 2023-02-17 11:16:42 -08:00
Mathias Agopian d9266ba61c fix a main camera far plane culling
we need to use the culling projection matrix for culling!
2023-02-16 15:40:32 -08:00
Ben Doherty 8389056d2d gltfio: Fix crash when a MIME type has no texture provider (#6569) 2023-02-16 14:21:26 -08:00
Powei Feng 26b8cb238a Add build.sh flag for ASAN/UBSAN (#6566) 2023-02-16 13:19:32 -08:00
Powei Feng f8d4690cd8 Update linux-continuous.yml to use ubuntu-22.04 (#6559) 2023-02-14 16:47:02 -08:00
Benjamin Doherty c2e2f80945 Fix RELEASE_NOTES.md 2023-02-14 14:10:40 -08:00
Powei Feng e9efcdf191 -Werror for libfilament and backend (#6547) 2023-02-14 01:15:25 -08:00
Mathias Agopian 8ea159520d workaround our -ffast-math
We use isnan() to detect that the estimation of directional light
parameters from the IBL has failed, however isnan() always returns
false with -ffast-math, which we're using in release builds.
This works around that. The affected code is non essential, performance
is not a concern here.
2023-02-13 21:51:43 -08:00
Mathias Agopian 337e18051a The default render channel is now 2 instead of 0
This also fixes some potential issues with refraction when using
render channels.
2023-02-13 17:31:34 -08:00
Ben Doherty 3c4e094990 Rework how release notes work (#6557) 2023-02-13 14:54:07 -08:00
Powei Feng 71e6f3d037 Update linux clang to version 10 (#6556) 2023-02-13 14:49:45 -08:00
Benjamin Doherty 092a0489da Bump version to 1.31.5 2023-02-13 11:53:24 -08:00
Benjamin Doherty 1c6279366f Merge branch 'rc/1.31.4' into release 2023-02-13 11:51:37 -08:00
Benjamin Doherty d2690bf75a Release Filament 1.31.4 2023-02-13 11:51:06 -08:00
Ben Doherty 44f002a5a1 gltfio: Fix race condition affecting mobile and web (#6548)
When loading a glTF file on platforms without a filesystem, a client
calls `addResourceData` to populate `ResourceLoader`'s cache with data.
For example, a web client might make HTTP fetch requests to fill in
buffer data. This internal cache of data is stored in
`ResourceLoader::Impl::mUriDataCache`.

This works well, except the cache must persist until after it has been
uploaded to the GPU.

There was already a mechanism (see `uploadUserdata`) in place to ensure
that glTF data persisted until after it had been uploaded to the GPU.
However, this mechanism did not extend to client-provided data. Thus, a
race occured between Filament's driver consuming the buffer and it
getting freed.
2023-02-13 10:44:22 -08:00
Powei Feng f8442c9ec0 vulkan: Remove unused presentQueueFamilyIndex (#6545) 2023-02-10 12:41:00 -08:00
Powei Feng b3513c7d1f geometry: Add TangentSpaceMesh implementations (#6530)
geometry: Add TangentSpaceMesh implementations

Added:
 - Hughes-Moller
 - Frisvad's method
 - Flat shading
2023-02-10 09:34:49 -08:00
Mathias Agopian a82e813333 fix libmath streaming operators 2023-02-09 16:11:30 -08:00
Powei Feng dd246aeee4 vulkan: Assert that graphics-QF is present-QF (#6540) 2023-02-09 13:31:29 -08:00
Ben Doherty 1653d3615b Fix Metal crash due to empty program names (#6537) 2023-02-09 12:11:04 -08:00
Mathias Agopian 4847b00f9e improve SoA a bit
Instead of storing the arrays into an array of void*, we use a 
tuple<> instead. This improves debugging because now the tuple<>
has pointer with the correct types.

It also improves most of the code except `push_back` which now
relies on a hack -- this is the only place where I'm not able to 
resolve the array strictly at compile time, even if in practice it is.
2023-02-08 16:22:00 -08:00
Igor Korobka d613145c1a Fix JNI after minification is applied
UbershaderProvider.getNativeObject() is accessed from AssetLoader.cpp,
so it must be annotated with @UsedByNative("AssetLoader.cpp") to
avoid runtime crashes when minification is applied.

Fixes #5944
2023-02-08 15:29:55 -08:00
ritik619 a788f66e18 Fixed typographic error 2023-02-08 12:01:50 -08:00
Romain Guy 9ca3a7456c Fix JNI 2023-02-08 09:07:59 -08:00
Mathias Agopian 5cec001058 matdbg: direct SPIRV edit now supported 2023-02-03 13:57:44 -08:00
Mathias Agopian 2fb42f5144 matdbg: refresh spirv properly
we now refresh the spirv code properly after it's rebuilt from the
glsl tab.
2023-02-02 11:10:21 -08:00
Mathias Agopian 02c22a668c matdbg: make sure we use the same spirv parameters everywhere 2023-02-02 11:10:02 -08:00
Mathias Agopian a7bf90f5be build.sh -d is now split in two options
`-d` now enables matdbg and adds debugging data, but doesn't affect 
 material optimization

`-g` disables material optimizations


A similar change is done with gradle options. The new proprety
`com.google.android.filament.matnopt` is used to disable material
optimizations.

These options mimic `matc` options.
2023-02-01 23:59:00 -08:00
Mathias Agopian f71e90fae0 deduplicate spirv disassembly code 2023-02-01 21:12:53 -08:00
Mathias Agopian 8afb11128d move UibGenerator and SibGenerator to the shader folder
The shader folder is where we generate the glsl, so this is more
appropriate.
2023-02-01 21:12:53 -08:00
Mathias Agopian 2e0c238cc6 We now require Vulkan 1.1 for the vulkan backend 2023-02-01 21:10:26 -08:00
Benjamin Doherty 2ac85049a9 Bump version to 1.31.4 2023-02-01 11:55:09 -08:00
Benjamin Doherty 03b89b4327 Release Filament 1.31.3 2023-02-01 11:53:41 -08:00
Mathias Agopian a878d1f39c improve sample_full_pbr
- -m option now works with "directory_*.png" or just "*.png"
- "color" replaced by "albedo" to mach other places in the source tree
- fixed warnings
- clear the background when IBL is not used
2023-02-01 10:04:02 -08:00
Mathias Agopian 4a09e13e9d fix a couple typos and consts.
make it easier to activate matdbg on android builds, by adding, but
commenting out the appropriate property.
2023-02-01 10:03:20 -08:00
Mathias Agopian 0b4d32bf98 matdbg: use GLSL 4.5 on desktop when debugging spirv
GLSL 4.5 is closer to spriv than 4.1 and since this is just to 
"interpret" the spirv, 4.5 is fine.

Set the default precision for GLES though.
2023-02-01 10:03:20 -08:00
Mathias Agopian b940fc9c48 update remote ui 2023-01-31 20:55:23 -08:00
Koncz Levente 2eb0dd7153 Sample Full PBR fixes (#6490)
* Calculate view vector without using camera eye position

* Fix compilation error (during parsing the material when a height map is passed in)

* Add SUN light to the scene
2023-01-31 12:28:25 -08:00
Mathias Agopian 101c7db9a2 update remote ui 2023-01-31 09:52:37 -08:00
Mathias Agopian 032ace2051 Add output color space to ColorGrading settings 2023-01-31 09:46:22 -08:00
Mathias Agopian 5754b9907d Add support for sRGB swapchains
This change adds a 'SRGB' config flag when creating a SwapChain that
enables linear to sRGB conversion on write.

When using this flag, the linear->srgb conversion in the color grading
post processing should be disabled (or, the whole post-processing
stage should be disabled).

There is also a new query to determine if this flag is supported by
the underlaying platform.

On Metal, this happens automatically when the underlaying layer is sRGB.

This reverts commit 3799e219fc.
2023-01-31 09:29:02 -08:00
Mathias Agopian ffb8203ae7 Fix typo in ColorSpace operator= 2023-01-30 21:35:51 -08:00
Powei Feng 906da033dd vulkan: Add fallback extent for swap chain sizing (#6495)
vulkan: Add fallback extent for swap chain sizing
2023-01-30 19:29:10 -08:00
daemyung danny jang 1077405bc1 Support Windows32 on bluegl as C backend (#6499) 2023-01-30 15:56:15 -08:00
daemyung danny jang e37df8fc89 Include a header in alphabetical order (#6507) 2023-01-30 14:58:56 -08:00
Mathias Agopian 3799e219fc Revert "Add support for sRGB swapchains"
This reverts commit eab14da30e.

This broke macOS desktop (and maybe other GL platforms).
2023-01-30 11:20:18 -08:00
Mathias Agopian eab14da30e Add support for sRGB swapchains
This change adds a 'SRGB' config flag when creating a SwapChain that
enables linear to sRGB conversion on write.

When using this flag, the linear->srgb conversion in the color grading
post processing should be disabled (or, the whole post-processing
stage should be disabled).

There is also a new query to determine if this flag is supported by
the underlaying platform.


On Metal, this happens automatically when the underlaying layer is sRGB.
2023-01-30 10:49:38 -08:00
Mathias Agopian 84e999fe57 cleanup PlatformEGL config selection code
We now structure the code to assume we have EGL_KHR_no_config_context,
which is the common case. This allows use to decouple context creation
from swapchain creation.

We still handle the case where the extension is not present by always
selecting a transparent config.
2023-01-30 10:49:38 -08:00
Mathias Agopian f22ff6a340 the createSwapChain flag shouldn't be a reference 2023-01-30 10:49:38 -08:00
daemyung danny jang d9d0b93306 Fix a build error (#6502) 2023-01-30 10:38:36 -08:00
Powei Feng 09248cd451 Destroy FilamentInstance's mRoot in EntityManager (#6500) 2023-01-27 16:05:42 -08:00
Koncz Levente 73880428dc Ortho shading fixes (#6453) 2023-01-27 09:36:56 -08:00
Ben Doherty 2a049d1324 Initialize object_uniforms in depth shader (#6492) 2023-01-26 16:23:14 -08:00
Powei Feng 51cbd74235 Add missing STL includes (#6496) 2023-01-26 16:07:58 -08:00
Powei Feng 45246b6109 geometry: Add TangentSpaceMesh class outline (#6476)
- Defined class with documentation in comments
 - Implemented algorithm selection
 - No actual algorithms written yet

Part of google/filament#6358
2023-01-26 14:39:48 -08:00
Ben Doherty d3956adca8 matdbg: take desktop/mobile into account for SPIR-V to GLSL translation (#6488) 2023-01-26 11:30:51 -08:00
226 changed files with 119954 additions and 99569 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-linux:
name: build-linux
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3.3.0
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-18.04]
os: [macos-latest, ubuntu-22.04]
steps:
- uses: actions/checkout@v3.3.0
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-18.04]
os: [macos-latest, ubuntu-22.04]
steps:
- name: Decide Git ref
@@ -24,3 +24,4 @@ jobs:
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ github.event.pull_request.number }}
release-notes-file: 'NEW_RELEASE_NOTES.md'
+37 -24
View File
@@ -31,6 +31,8 @@ option(FILAMENT_SKIP_SDL2 "Skip dependencies of SDL2, and SDL2" OFF)
option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
@@ -67,25 +69,30 @@ endif()
# ==================================================================================================
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
set(C_LAUNCHER "${CCACHE_PROGRAM}")
set(CXX_LAUNCHER "${CCACHE_PROGRAM}")
configure_file(build/launch-c.in launch-c)
configure_file(build/launch-cxx.in launch-cxx)
execute_process(COMMAND chmod a+rx
"${CMAKE_CURRENT_BINARY_DIR}/launch-c"
"${CMAKE_CURRENT_BINARY_DIR}/launch-cxx"
)
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
if (WIN32)
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
else()
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(C_LAUNCHER "${CCACHE_PROGRAM}")
set(CXX_LAUNCHER "${CCACHE_PROGRAM}")
configure_file(build/launch-c.in launch-c)
configure_file(build/launch-cxx.in launch-cxx)
execute_process(COMMAND chmod a+rx
"${CMAKE_CURRENT_BINARY_DIR}/launch-c"
"${CMAKE_CURRENT_BINARY_DIR}/launch-cxx"
)
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
else()
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
endif()
endif()
endif()
@@ -374,10 +381,10 @@ endif()
# ==================================================================================================
# Debug compiler flags
# ==================================================================================================
# ASAN is deactivated for now because:
# -fsanitize=undefined causes extremely long link times
# -fsanitize=address causes a crash with assimp, which we can't explain for now
#set(EXTRA_SANITIZE_OPTIONS "-fsanitize=undefined -fsanitize=address")
if (FILAMENT_ENABLE_ASAN_UBSAN)
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=address -fsanitize=undefined")
endif()
if (NOT MSVC AND NOT WEBGL)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector")
endif()
@@ -497,9 +504,14 @@ if (FILAMENT_SUPPORTS_METAL)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
endif()
# Disable optimizations and enable debug info (preserves names in SPIR-V)
# Enable debug info (preserves names in SPIR-V)
if (FILAMENT_ENABLE_MATDBG)
set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d)
endif()
# Disable optimizations
if (FILAMENT_DISABLE_MATOPT)
set(MATC_OPT_FLAGS -gd)
set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -g)
endif()
set(MATC_BASE_FLAGS ${MATC_API_FLAGS} -p ${MATC_TARGET} ${MATC_OPT_FLAGS})
@@ -675,6 +687,7 @@ add_subdirectory(${EXTERNAL}/robin-map/tnt)
add_subdirectory(${EXTERNAL}/smol-v/tnt)
add_subdirectory(${EXTERNAL}/benchmark/tnt)
add_subdirectory(${EXTERNAL}/meshoptimizer/tnt)
add_subdirectory(${EXTERNAL}/mikktspace)
add_subdirectory(${EXTERNAL}/cgltf/tnt)
add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
+9
View File
@@ -0,0 +1,9 @@
# Filament Release Notes log
**If you are merging a PR into main**: please add the release note below, under the *Release notes
for next branch cut* header.
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.31.3'
implementation 'com.google.android.filament:filament-android:1.32.0'
}
```
@@ -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:
```
pod 'Filament', '~> 1.31.3'
pod 'Filament', '~> 1.32.0'
```
### Snapshots
+30 -1
View File
@@ -3,7 +3,36 @@
This file contains one line summaries of commits that are worthy of mentioning in release notes.
A new header is inserted each time a *tag* is created.
## main branch
**Do not edit this file unless you are performing a release or cherry-picking into an rc/ branch.**
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.32.0
- fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**]
- materials: new alphaToCoverage property can be used to control alpha to coverage behavior
- materials: added `getUserWorldFromWorldMatrix()` and `getUserWorldPosition()` to retrieve the
API-level (user) world position in materials. Deprecated `getWorldOffset()`. [⚠️ **Recompile
Materials**]
- engine: fix precision issue with `shading_view` in large scenes
- vulkan: readPixels is now async (#6560)
## v1.31.7
## v1.31.6
- engine: the default render channel is now 2 instead of 0
- gltfio: Fix crash when a MIME type has no texture provider
## v1.31.5
- gltfio: fix potential early freeing of data provided with `ResourceLoader::addResourceData`.
## v1.31.4
- engine: fix broken picking [⚠️ **Recompile Materials to get the fix**]
- engine: added support for sRGB swapchains. See `SwapChain.h`
- bluegl: support Windows32
## v1.31.3
+12 -2
View File
@@ -12,7 +12,10 @@
// When set, support for Vulkan will be excluded.
//
// com.google.android.filament.matdbg
// When set, enables matdbg, disables shader optimizations
// When set, enables matdbg
//
// com.google.android.filament.matnopt
// When set, disable shader optimizations.
//
// com.google.android.filament.skip-samples
// Exclude samples from the project. Useful to speed up compilation.
@@ -31,6 +34,9 @@
// - Build and upload artifacts with ./gradlew publish
// - Close and release staging repo on Nexus with ./gradlew closeAndReleaseStagingRepository
//
// These commands must both be run in the same Gradle invocation:
// ./gradlew publish closeAndReleaseStagingRepository
//
// The following is needed in ~/gradle/gradle.properties:
//
// sonatypeUsername=nexus_user
@@ -62,6 +68,10 @@ buildscript {
.gradleProperty("com.google.android.filament.matdbg")
.isPresent()
def matnopt = providers
.gradleProperty("com.google.android.filament.matnopt")
.isPresent()
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
def newAbis = providers
.gradleProperty("com.google.android.filament.abis")
@@ -108,7 +118,7 @@ buildscript {
"-DFILAMENT_DIST_DIR=${filamentPath}".toString(),
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(),
"-DFILAMENT_ENABLE_MATDBG=${matdbg ? 'ON' : 'OFF'}".toString(),
"-DFILAMENT_DISABLE_MATOPT=${matdbg ? 'ON' : 'OFF'}".toString()
"-DFILAMENT_DISABLE_MATOPT=${matnopt ? 'ON' : 'OFF'}".toString()
]
ext.cppFlags = [
@@ -250,6 +250,13 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderMaskThr
builder->maskThreshold(maskThreshold);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderAlphaToCoverage(JNIEnv*,
jclass, jlong nativeBuilder, jboolean enable) {
auto builder = (MaterialBuilder*) nativeBuilder;
builder->alphaToCoverage(enable);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderShadowMultiplier(
JNIEnv*, jclass, jlong nativeBuilder, jboolean shadowMultiplier) {
@@ -360,6 +360,12 @@ public class MaterialBuilder {
return this;
}
@NonNull
public MaterialBuilder alphaToCoverage(boolean enable) {
nMaterialBuilderAlphaToCoverage(mNativeObject, enable);
return this;
}
@NonNull
public MaterialBuilder shadowMultiplier(boolean shadowMultiplier) {
nMaterialBuilderShadowMultiplier(mNativeObject, shadowMultiplier);
@@ -584,6 +590,7 @@ public class MaterialBuilder {
private static native void nMaterialBuilderDepthCulling(long nativeBuilder, boolean enable);
private static native void nMaterialBuilderDoubleSided(long nativeBuilder, boolean doubleSided);
private static native void nMaterialBuilderMaskThreshold(long nativeBuilder, float mode);
private static native void nMaterialBuilderAlphaToCoverage(long nativeBuilder, boolean enable);
private static native void nMaterialBuilderShadowMultiplier(long mNativeObject,
boolean shadowMultiplier);
@@ -153,6 +153,14 @@ Java_com_google_android_filament_Material_nIsDoubleSided(JNIEnv*, jclass,
return (jboolean) material->isDoubleSided();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Material_nIsAlphaToCoverageEnabled(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jboolean) material->isAlphaToCoverageEnabled();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Material_nGetMaskThreshold(JNIEnv*, jclass,
@@ -457,7 +457,6 @@ extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nGetMaskThreshold()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getMaskThreshold();
}
@@ -466,7 +465,6 @@ extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env,
jclass clazz, jlong nativeMaterialInstance) {
// TODO: implement nGetSpecularAntiAliasingVariance()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getSpecularAntiAliasingVariance();
}
@@ -475,7 +473,6 @@ extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env,
jclass clazz, jlong nativeMaterialInstance) {
// TODO: implement nGetSpecularAntiAliasingThreshold()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getSpecularAntiAliasingThreshold();
}
@@ -484,7 +481,6 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDoubleSided()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDoubleSided();
}
@@ -493,7 +489,6 @@ extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nGetCullingMode()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return (jint)instance->getCullingMode();
}
@@ -502,7 +497,6 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsColorWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isColorWriteEnabled();
}
@@ -511,7 +505,6 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDepthWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDepthWriteEnabled();
}
@@ -520,7 +513,6 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsStencilWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isStencilWriteEnabled();
}
@@ -529,7 +521,6 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDepthCullingEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDepthCullingEnabled();
}
@@ -16,6 +16,7 @@
#include <jni.h>
#include <filament/Engine.h>
#include <filament/SwapChain.h>
#include "common/CallbackUtils.h"
@@ -32,3 +33,9 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
JniCallback::postToJavaAndDestroy(callback);
}, callback);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(JNIEnv *, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (bool)SwapChain::isSRGBSwapChainSupported(*engine);
}
@@ -503,6 +503,17 @@ public class Material {
return nIsDoubleSided(getNativeObject());
}
/**
* Indicates whether instances of this material will use alpha to coverage.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:alphatocoverage">
* Rasterization: alphaToCoverage</a>
*/
public boolean isAlphaToCoverageEnabled() {
return nIsAlphaToCoverageEnabled(getNativeObject());
}
/**
* Returns the alpha mask threshold used when the blending mode is set to masked.
*
@@ -915,6 +926,7 @@ public class Material {
private static native boolean nIsDepthWriteEnabled(long nativeMaterial);
private static native boolean nIsDepthCullingEnabled(long nativeMaterial);
private static native boolean nIsDoubleSided(long nativeMaterial);
private static native boolean nIsAlphaToCoverageEnabled(long nativeMaterial);
private static native float nGetMaskThreshold(long nativeMaterial);
private static native float nGetSpecularAntiAliasingVariance(long nativeMaterial);
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial);
@@ -307,11 +307,16 @@ public class RenderableManager {
/**
* Set the channel this renderable is associated to. There can be 4 channels.
* All renderables in a given channel are rendered together, regardless of anything else.
* They are sorted as usual withing a channel.
* Channels work similarly to priorities, except that they enforce the strongest ordering.
*
* @param channel clamped to the range [0..3], defaults to 0.
* <p>All renderables in a given channel are rendered together, regardless of anything else.
* They are sorted as usual within a channel.</p>
* <p>Channels work similarly to priorities, except that they enforce the strongest
* ordering.</p>
*
* <p>Channels 0 and 1 may not have render primitives using a material with `refractionType`
* set to `screenspace`.</p>
*
* @param channel clamped to the range [0..3], defaults to 2.
*
* @return Builder reference for chaining calls.
*
@@ -91,11 +91,34 @@ public class SwapChain {
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
}
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
}
/**
* @return the native <code>Object</code> this <code>SwapChain</code> was created from or null
* for a headless SwapChain.
@@ -141,4 +164,5 @@ public class SwapChain {
}
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
}
@@ -276,7 +276,7 @@ public class TextureSampler {
}
/**
* Sets the wrapping mode in the t (depth) direction.
* Sets the wrapping mode in the r (depth) direction.
* @param mode wrapping mode
*/
public void setWrapModeR(WrapMode mode) {
@@ -166,6 +166,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv
const jfieldID cameraAperture = env->GetFieldID(klass, "cameraAperture", "F");
const jfieldID cameraSpeed = env->GetFieldID(klass, "cameraSpeed", "F");
const jfieldID cameraISO = env->GetFieldID(klass, "cameraISO", "F");
const jfieldID cameraNear = env->GetFieldID(klass, "cameraNear", "F");
const jfieldID cameraFar = env->GetFieldID(klass, "cameraFar", "F");
const jfieldID groundShadowStrength = env->GetFieldID(klass, "groundShadowStrength", "F");
const jfieldID groundPlaneEnabled = env->GetFieldID(klass, "groundPlaneEnabled", "Z");
const jfieldID skyboxEnabled = env->GetFieldID(klass, "skyboxEnabled", "Z");
@@ -177,6 +179,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv
env->SetFloatField(result, cameraAperture, options.cameraAperture);
env->SetFloatField(result, cameraSpeed, options.cameraSpeed);
env->SetFloatField(result, cameraISO, options.cameraISO);
env->SetFloatField(result, cameraNear, options.cameraNear);
env->SetFloatField(result, cameraFar, options.cameraFar);
env->SetFloatField(result, groundShadowStrength, options.groundShadowStrength);
env->SetBooleanField(result, groundPlaneEnabled, options.groundPlaneEnabled);
env->SetBooleanField(result, skyboxEnabled, options.skyboxEnabled);
@@ -97,6 +97,8 @@ public class AutomationEngine {
public float cameraAperture = 16.0f;
public float cameraSpeed = 125.0f;
public float cameraISO = 100.0f;
public float cameraNear = 0.1f;
public float cameraFar = 100.0f;
public float groundShadowStrength = 0.75f;
public boolean groundPlaneEnabled = false;
public boolean skyboxEnabled = true;
@@ -27,8 +27,8 @@ import com.google.android.filament.gltfio.*
import kotlinx.coroutines.*
import java.nio.Buffer
private const val kNearPlane = 0.05 // 5 cm
private const val kFarPlane = 1000.0 // 1 km
private const val kNearPlane = 0.05f // 5 cm
private const val kFarPlane = 1000.0f // 1 km
private const val kAperture = 16f
private const val kShutterSpeed = 1f / 125f
private const val kSensitivity = 100f
@@ -80,6 +80,18 @@ class ModelViewer(
updateCameraProjection()
}
var cameraNear = kNearPlane
set(value) {
field = value
updateCameraProjection()
}
var cameraFar = kFarPlane
set(value) {
field = value
updateCameraProjection()
}
val scene: Scene
val view: View
val camera: Camera
@@ -368,7 +380,8 @@ class ModelViewer(
val width = view.viewport.width
val height = view.viewport.height
val aspect = width.toDouble() / height.toDouble()
camera.setLensProjection(cameraFocalLength.toDouble(), aspect, kNearPlane, kFarPlane)
camera.setLensProjection(cameraFocalLength.toDouble(), aspect,
cameraNear.toDouble(), cameraFar.toDouble())
}
inner class SurfaceCallback : UiHelper.RendererCallback {
@@ -28,47 +28,81 @@ import com.google.android.filament.proguard.UsedByNative;
@UsedByNative("AssetLoader.cpp")
public interface MaterialProvider {
/**
* MaterialKey specifies the requirements for a requested glTF material.
* The provider creates Filament materials that fulfill these requirements.
*/
@UsedByNative("MaterialKey.cpp")
public static class MaterialKey {
@UsedByNative("MaterialKey.cpp")
public boolean doubleSided;
@UsedByNative("MaterialKey.cpp")
public boolean unlit;
@UsedByNative("MaterialKey.cpp")
public boolean hasVertexColors;
@UsedByNative("MaterialKey.cpp")
public boolean hasBaseColorTexture;
@UsedByNative("MaterialKey.cpp")
public boolean hasNormalTexture;
@UsedByNative("MaterialKey.cpp")
public boolean hasOcclusionTexture;
@UsedByNative("MaterialKey.cpp")
public boolean hasEmissiveTexture;
@UsedByNative("MaterialKey.cpp")
public boolean useSpecularGlossiness;
@UsedByNative("MaterialKey.cpp")
public int alphaMode; // 0 = OPAQUE, 1 = MASK, 2 = BLEND
@UsedByNative("MaterialKey.cpp")
public boolean enableDiagnostics;
@UsedByNative("MaterialKey.cpp")
public boolean hasMetallicRoughnessTexture; // piggybacks with specularRoughness
@UsedByNative("MaterialKey.cpp")
public int metallicRoughnessUV; // piggybacks with specularRoughness
@UsedByNative("MaterialKey.cpp")
public int baseColorUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasClearCoatTexture;
@UsedByNative("MaterialKey.cpp")
public int clearCoatUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasClearCoatRoughnessTexture;
@UsedByNative("MaterialKey.cpp")
public int clearCoatRoughnessUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasClearCoatNormalTexture;
@UsedByNative("MaterialKey.cpp")
public int clearCoatNormalUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasClearCoat;
@UsedByNative("MaterialKey.cpp")
public boolean hasTransmission;
@UsedByNative("MaterialKey.cpp")
public boolean hasTextureTransforms;
@UsedByNative("MaterialKey.cpp")
public int emissiveUV;
@UsedByNative("MaterialKey.cpp")
public int aoUV;
@UsedByNative("MaterialKey.cpp")
public int normalUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasTransmissionTexture;
@UsedByNative("MaterialKey.cpp")
public int transmissionUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasSheenColorTexture;
@UsedByNative("MaterialKey.cpp")
public int sheenColorUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasSheenRoughnessTexture;
@UsedByNative("MaterialKey.cpp")
public int sheenRoughnessUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasVolumeThicknessTexture;
@UsedByNative("MaterialKey.cpp")
public int volumeThicknessUV;
@UsedByNative("MaterialKey.cpp")
public boolean hasSheen;
@UsedByNative("MaterialKey.cpp")
public boolean hasIOR;
public MaterialKey() {}
@@ -20,6 +20,7 @@ import com.google.android.filament.Engine;
import com.google.android.filament.MaterialInstance;
import com.google.android.filament.Material;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.proguard.UsedByNative;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -94,6 +95,7 @@ public class UbershaderProvider implements MaterialProvider {
nDestroyMaterials(mNativeObject);
}
@UsedByNative("AssetLoader.cpp")
public long getNativeObject() {
return mNativeObject;
}
+3 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.31.3
VERSION_NAME=1.32.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -22,3 +22,5 @@ android.useAndroidX=true
com.google.android.filament.tools-dir=../../../out/release/filament
com.google.android.filament.dist-dir=../out/android-release/filament
com.google.android.filament.abis=all
#com.google.android.filament.matdbg
#com.google.android.filament.matnopt
@@ -356,6 +356,8 @@ class MainActivity : Activity() {
automation.applySettings(modelViewer.engine, json, viewerContent)
modelViewer.view.colorGrading = automation.getColorGrading(modelViewer.engine)
modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength
modelViewer.cameraNear = automation.viewerOptions.cameraNear
modelViewer.cameraFar = automation.viewerOptions.cameraFar
updateRootTransform()
}
@@ -1,3 +1,3 @@
<resources>
<string name="app_name">Image-Based Lighting</string>
<string name="app_name">Material Builder</string>
</resources>

Before

Width:  |  Height:  |  Size: 848 KiB

After

Width:  |  Height:  |  Size: 848 KiB

+26 -3
View File
@@ -22,9 +22,11 @@ function print_help {
echo " All (and only) git-ignored files under android/ are deleted."
echo " This is sometimes needed instead of -c (which still misses some clean steps)."
echo " -d"
echo " Enable matdbg and disable material optimization."
echo " Enable matdbg."
echo " -f"
echo " Always invoke CMake before incremental builds."
echo " -g"
echo " Disable material optimization."
echo " -i"
echo " Install build output"
echo " -m"
@@ -56,6 +58,9 @@ function print_help {
echo " When building for Android, also build select sample APKs."
echo " sampleN is an Android sample, e.g., sample-gltf-viewer."
echo " This automatically performs a partial desktop build and install."
echo " -b"
echo " Enable Address and Undefined Behavior Sanitizers (asan/ubsan) for debugging."
echo " This is only for the desktop build."
echo ""
echo "Build types:"
echo " release"
@@ -162,6 +167,11 @@ EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
MATOPT_OPTION=""
MATOPT_GRADLE_OPTION=""
ASAN_UBSAN_OPTION=""
IOS_BUILD_SIMULATOR=false
BUILD_UNIVERSAL_LIBRARIES=false
@@ -221,6 +231,8 @@ function build_desktop_target {
${SWIFTSHADER_OPTION} \
${EGL_ON_LINUX_OPTION} \
${MATDBG_OPTION} \
${MATOPT_OPTION} \
${ASAN_UBSAN_OPTION} \
${deployment_target} \
${architectures} \
../..
@@ -347,6 +359,7 @@ function build_android_target {
-DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \
-DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \
${MATDBG_OPTION} \
${MATOPT_OPTION} \
${VULKAN_ANDROID_OPTION} \
../..
fi
@@ -463,6 +476,7 @@ function build_android {
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug \
:filament-utils-android:assembleDebug
@@ -510,6 +524,7 @@ function build_android {
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease \
:filament-utils-android:assembleRelease
@@ -576,6 +591,7 @@ function build_ios_target {
-DIOS=1 \
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
${MATDBG_OPTION} \
${MATOPT_OPTION} \
../..
fi
@@ -740,7 +756,7 @@ function run_tests {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
while getopts ":hacCfijmp:q:uvslwtedk:b" opt; do
case ${opt} in
h)
print_help
@@ -758,12 +774,16 @@ while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
;;
d)
PRINT_MATDBG_HELP=true
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON, -DFILAMENT_BUILD_FILAMAT=ON"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_BUILD_FILAMAT=ON"
MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg"
;;
f)
ISSUE_CMAKE_ALWAYS=true
;;
g)
MATOPT_OPTION="-DFILAMENT_DISABLE_MATOPT=ON"
MATOPT_GRADLE_OPTION="-Pcom.google.android.filament.matnopt"
;;
i)
INSTALL_COMMAND=install
;;
@@ -863,6 +883,9 @@ while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
BUILD_ANDROID_SAMPLES=true
ANDROID_SAMPLES=$(echo "${OPTARG}" | tr ',' '\n')
;;
b) ASAN_UBSAN_OPTION="-DFILAMENT_ENABLE_ASAN_UBSAN=ON"
echo "Enabled ASAN/UBSAN"
;;
\?)
echo "Invalid option: -${OPTARG}" >&2
echo ""
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# version of clang we want to use
GITHUB_CLANG_VERSION=8
GITHUB_CLANG_VERSION=14
# version of CMake to use instead of the default one
CMAKE_VERSION=3.19.5
# version of ninja to use
+48 -8
View File
@@ -1362,6 +1362,11 @@ Description
ALPHA_TO_COVERAGE is enabled for non-translucent views. See the maskThreshold section for more
information.
!!! Note
When `blending` is set to `masked`, alpha to coverage is automatically enabled for the material.
If this behavior is undesirable, refer to the Rasterization: alphaToCoverage section to turn
alpha to coverage off using the `alphaToCoverage` property.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
blending : transparent
@@ -1602,6 +1607,37 @@ fragment {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Rasterization: alphaToCoverage
Type
: `boolean`
Value
: `true` or `false`. Defaults to `false`.
Description
: Enables or disables alpha to coverage. When alpha to coverage is enabled, the coverage of
fragment is derived from its alpha. This property is only meaningful when MSAA is enabled.
Note: setting `blending` to `masked` automatically enables alpha to coverage. If this is not
desired, you can override this behavior by setting alpha to coverage to false as in the
example below.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
name : "Alpha to coverage",
shadingModel : lit,
blending : masked,
alphaToCoverage : false
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.albedo;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Lighting: reflections
Type
@@ -1910,8 +1946,9 @@ struct MaterialVertexInputs {
!!! TIP: worldPosition
To achieve good precision, the `worldPosition` coordinate in the vertex shader is shifted by the
camera position. To get the true world-space position, users can add this to
`getWorldOffset()`.
camera position. To get the true world-space position, users can use
`getUserWorldPosition()`, however be aware that the true world-position might not
be able to fit in a `float` or might be represented with severely reduced precision.
!!! TIP: UV attributes
By default the vertex shader of a material will flip the Y coordinate of the UV attributes
@@ -2264,8 +2301,9 @@ type aliases:
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getResolution()** | float4 | Dimensions of the view's effective (physical) viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands.
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space
**getWorldOffset()** | float3 | The shift required to obtain API-level world space
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space (see note below)
**getWorldOffset()** | float3 | [deprecated] The shift required to obtain API-level world space. Use getUserWorldPosition() instead
**getUserWorldFromWorldMatrix()** | float4x4 | Matrix that converts from world space to API-level (user) world space.
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
**getUserTime()** | float4 | Current time in seconds: `time`, `(double)time - time`, `0`, `0`
**getUserTimeMode(float m)** | float | Current time modulo m in seconds
@@ -2275,7 +2313,7 @@ type aliases:
!!! TIP: world space
To achieve good precision, the "world space" in Filament's shading system does not necessarily
match the API-level world space. To obtain the position of the API-level camera, custom
materials can add `getWorldOffset()` to `getWorldCameraPosition()`.
materials can use `getUserWorldFromWorldMatrix()` to transform `getWorldCameraPosition()`.
### Vertex only
@@ -2297,6 +2335,7 @@ The following APIs are only available from the fragment block:
:---------------------------------------|:--------:|:------------------------------------
**getWorldTangentFrame()** | float3x3 | Matrix containing in each column the `tangent` (`frame[0]`), `bi-tangent` (`frame[1]`) and `normal` (`frame[2]`) of the vertex in world space. If the material does not compute a tangent space normal for bump mapping or if the shading is not anisotropic, only the `normal` is valid in this matrix.
**getWorldPosition()** | float3 | Position of the fragment in world space (see note below about world-space)
**getUserWorldPosition()** | float3 | Position of the fragment in API-level (user) world-space (see note below about world-space)
**getWorldViewVector()** | float3 | Normalized vector in world space from the fragment position to the eye
**getWorldNormalVector()** | float3 | Normalized normal in world space, after bump mapping (must be used after `prepareMaterial()`)
**getWorldGeometricNormalVector()** | float3 | Normalized normal in world space, before bump mapping (can be used before `prepareMaterial()`)
@@ -2313,9 +2352,10 @@ The following APIs are only available from the fragment block:
**ycbcrToRgb(float, float2)** | float3 | Converts a luminance and CbCr pair to a sRGB color
**uvToRenderTargetUV(float2)** | float2 | Transforms a UV coordinate to allow sampling from a `RenderTarget` attachment
!!! TIP: world space
To obtain API-level world space coordinates, custom materials should add `getWorldOffset()` to
`getWorldPosition()` (et al).
!!! TIP: world-space
To obtain API-level world-space coordinates, custom materials should use `getUserWorldPosition()`
or use `getUserWorldFromWorldMatrix()`. Note that API-level world-space coordinates should
never or rarely be used because they may not fit in a float3 or have severely reduced precision.
!!! TIP: sampling from render targets
When sampling from a `filament::Texture` that is attached to a `filament::RenderTarget` for
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+145 -145
View File
@@ -44,7 +44,7 @@ fragment {
</pre></div>
<p>Next, invoke <code>matc</code> as follows.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o plastic.filamat plastic.mat
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc<span style="color: #bbbbbb"> </span>-a<span style="color: #bbbbbb"> </span>opengl<span style="color: #bbbbbb"> </span>-p<span style="color: #bbbbbb"> </span>mobile<span style="color: #bbbbbb"> </span>-o<span style="color: #bbbbbb"> </span>plastic.filamat<span style="color: #bbbbbb"> </span>plastic.mat
</pre></div>
<p>You should now have a material archive in your working directory, which we&#x27;ll use later in the
@@ -53,7 +53,7 @@ tutorial.</p>
<p>Next we&#x27;ll use Filament&#x27;s <code>cmgen</code> tool to consume a HDR environment map in latlong format, and
produce two cubemap files: a mipmapped IBL and a blurry skybox.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/pillars_2k.hdr">pillars_2k.hdr</a>, then invoke the following command in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x pillars_2k --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 pillars_2k.hdr
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen<span style="color: #bbbbbb"> </span>-x<span style="color: #bbbbbb"> </span>pillars_2k<span style="color: #bbbbbb"> </span>--format<span style="color: #666666">=</span>ktx<span style="color: #bbbbbb"> </span>--size<span style="color: #666666">=256</span><span style="color: #bbbbbb"> </span>--extract-blur<span style="color: #666666">=0</span>.1<span style="color: #bbbbbb"> </span>pillars_2k.hdr
</pre></div>
<p>You should now have a <code>pillars_2k</code> folder containing a couple KTX files for the IBL and skybox, as
@@ -61,81 +61,81 @@ well as a text file with spherical harmonics coefficients. You can discard the t
IBL KTX contains these coefficients in its metadata.</p>
<h2>Create JavaScript</h2>
<p>Next, create <code>redball.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;pillars_2k&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;plastic.filamat&#39;</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;pillars_2k&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;plastic.filamat&#39;</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>ibl_url,<span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.PrimitiveType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.IndexType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer$IndexType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.LightType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.LightManager$Type;<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>ibl_url,<span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.PrimitiveType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType;
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.IndexType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer$IndexType;
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov;
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.LightType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.LightManager$Type;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(canvas);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(canvas);
}<span style="color: #bbbbbb"> </span>);
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create material</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create material</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>vec3.rotateY(eye,<span style="color: #bbbbbb"> </span>eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>radians);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10000</span>;
<span style="color: #bbbbbb"> </span>vec3.rotateY(eye,<span style="color: #bbbbbb"> </span>eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>radians);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>Fov.VERTICAL);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>Fov.VERTICAL);
<span style="color: #bbbbbb"> </span>}
}
</pre></div>
<p>The above boilerplate should be familiar to you from the previous tutorial, although it loads in a
new set of assets. We also added some animation to the camera.</p>
<p>Next let&#x27;s create a material instance from the package that we built at the beginning the tutorial.
Replace the <strong>create material</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(filamat_url);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>red<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.8</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>];<span style="color: #bbbbbb"></span>
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>,<span style="color: #bbbbbb"> </span>Filament.RgbType.sRGB,<span style="color: #bbbbbb"> </span>red);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.3</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>red<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.8</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>];
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>,<span style="color: #bbbbbb"> </span>Filament.RgbType.sRGB,<span style="color: #bbbbbb"> </span>red);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.3</span>);
</pre></div>
<p>The next step is to create a renderable for the sphere. To help with this, we&#x27;ll use the <code>IcoSphere</code>
@@ -149,33 +149,33 @@ as quaternions.</li>
</ul>
<p>Let&#x27;s go ahead use these arrays to build the vertex buffer and index buffer. Replace <strong>create
sphere</strong> with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>renderable<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(renderable);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>renderable<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();
scene.addEntity(renderable);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>icosphere<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.IcoSphere(<span style="color: #666666">5</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>icosphere<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.IcoSphere(<span style="color: #666666">5</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(icosphere.vertices.length<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT3,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.TANGENTS,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.SHORT4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.TANGENTS)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()
<span style="color: #bbbbbb"> </span>.vertexCount(icosphere.vertices.length<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>)
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT3,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.TANGENTS,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.SHORT4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.TANGENTS)
<span style="color: #bbbbbb"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(icosphere.triangles.length)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()
<span style="color: #bbbbbb"> </span>.indexCount(icosphere.triangles.length)
<span style="color: #bbbbbb"> </span>.bufferType(IndexType.USHORT)
<span style="color: #bbbbbb"> </span>.build(engine);
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>icosphere.vertices);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>icosphere.tangents);<span style="color: #bbbbbb"></span>
ib.setBuffer(engine,<span style="color: #bbbbbb"> </span>icosphere.triangles);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>icosphere.vertices);
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>icosphere.tangents);
ib.setBuffer(engine,<span style="color: #bbbbbb"> </span>icosphere.triangles);
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinstance)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span>vb,<span style="color: #bbbbbb"> </span>ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>renderable);<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinstance)
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span>vb,<span style="color: #bbbbbb"> </span>ib)
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>renderable);
</pre></div>
<p>At this point, the app is rendering a sphere, but it is black so it doesn&#x27;t show up. To prove that
@@ -185,94 +185,94 @@ did in the first tutorial.</p>
<p>In this section we will create some directional light sources, as well as an image-based light (IBL)
defined by one of the KTX files we built at the start of the demo. First, replace the <strong>create
lights</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sunlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(sunlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.SUN)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.color([<span style="color: #666666">0.98</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.92</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.89</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">110000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">0.6</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-0.8</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunAngularRadius(<span style="color: #666666">1.9</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloSize(<span style="color: #666666">10.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloFalloff(<span style="color: #666666">80.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>sunlight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sunlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();
scene.addEntity(sunlight);
Filament.LightManager.Builder(LightType.SUN)
<span style="color: #bbbbbb"> </span>.color([<span style="color: #666666">0.98</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.92</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.89</span>])
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">110000.0</span>)
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">0.6</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-0.8</span>])
<span style="color: #bbbbbb"> </span>.sunAngularRadius(<span style="color: #666666">1.9</span>)
<span style="color: #bbbbbb"> </span>.sunHaloSize(<span style="color: #666666">10.0</span>)
<span style="color: #bbbbbb"> </span>.sunHaloFalloff(<span style="color: #666666">80.0</span>)
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>sunlight);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>backlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(backlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.DIRECTIONAL)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>backlight);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>backlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();
scene.addEntity(backlight);
Filament.LightManager.Builder(LightType.DIRECTIONAL)
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>])
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>backlight);
</pre></div>
<p>The <code>SUN</code> light source is similar to the <code>DIRECTIONAL</code> light source, but has some extra
parameters because Filament will automatically draw a disk into the skybox.</p>
<p>Next we need to create an <code>IndirectLight</code> object from the KTX IBL. One way of doing this is the
following (don&#x27;t type this out, there&#x27;s an easier way).</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>format<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataFormat.RGB;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>datatype<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataType.UINT_10F_11F_11F_REV;<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>format<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataFormat.RGB;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>datatype<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataType.UINT_10F_11F_11F_REV;
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[ibl_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>iblktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(ibl_package);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[ibl_url]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>iblktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(ibl_package);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibltex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(iblktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(iblktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(iblktx.getNumMipLevels())<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibltex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()
<span style="color: #bbbbbb"> </span>.width(iblktx.info().pixelWidth)
<span style="color: #bbbbbb"> </span>.height(iblktx.info().pixelHeight)
<span style="color: #bbbbbb"> </span>.levels(iblktx.getNumMipLevels())
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)
<span style="color: #bbbbbb"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">for</span><span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">let</span><span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span>iblktx.getNumMipLevels();<span style="color: #bbbbbb"> </span><span style="color: #666666">++</span>level)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getCubeBlob(level).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>ibltex.setImageCube(engine,<span style="color: #bbbbbb"> </span>level,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">for</span><span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">let</span><span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span>iblktx.getNumMipLevels();<span style="color: #bbbbbb"> </span><span style="color: #666666">++</span>level)<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getCubeBlob(level).getBytes();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);
<span style="color: #bbbbbb"> </span>ibltex.setImageCube(engine,<span style="color: #bbbbbb"> </span>level,<span style="color: #bbbbbb"> </span>pixelbuffer);
}
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shstring<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shfloats<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>shstring.split(<span style="color: #A45A77">/\s/</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">9</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>).map(<span style="color: #008000">parseFloat</span>);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shstring<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shfloats<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>shstring.split(<span style="color: #A45A77">/\s/</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">9</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>).map(<span style="color: #008000">parseFloat</span>);
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndirectLight.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.reflections(ibltex)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.irradianceSh(<span style="color: #666666">3</span>,<span style="color: #bbbbbb"> </span>shfloats)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndirectLight.Builder()
<span style="color: #bbbbbb"> </span>.reflections(ibltex)
<span style="color: #bbbbbb"> </span>.irradianceSh(<span style="color: #666666">3</span>,<span style="color: #bbbbbb"> </span>shfloats)
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)
<span style="color: #bbbbbb"> </span>.build(engine);
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);
</pre></div>
<p>Filament provides a JavaScript utility to make this simpler,
simply replace the <strong>create IBL</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
indirectLight.setIntensity(<span style="color: #666666">50000</span>);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createIblFromKtx1(ibl_url);
indirectLight.setIntensity(<span style="color: #666666">50000</span>);
scene.setIndirectLight(indirectLight);
</pre></div>
<h2>Add background</h2>
<p>At this point you can run the demo and you should see a red plastic ball against a black background.
Without a skybox, the reflections on the ball are not representative of its surroundings.
Here&#x27;s one way to create a texture for the skybox:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[sky_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skyktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(sky_package);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skytex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(skyktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(skyktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[sky_url]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skyktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(sky_package);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skytex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()
<span style="color: #bbbbbb"> </span>.width(skyktx.info().pixelWidth)
<span style="color: #bbbbbb"> </span>.height(skyktx.info().pixelHeight)
<span style="color: #bbbbbb"> </span>.levels(<span style="color: #666666">1</span>)
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)
<span style="color: #bbbbbb"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
skytex.setImageCube(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);
skytex.setImageCube(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>pixelbuffer);
</pre></div>
<p>Filament provides a Javascript utility to make this easier.
Replace <strong>create skybox</strong> with the following.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSkyFromKtx1(sky_url);<span style="color: #bbbbbb"></span>
scene.setSkybox(skybox);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSkyFromKtx1(sky_url);
scene.setSkybox(skybox);
</pre></div>
<p>That&#x27;s it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
+108 -108
View File
@@ -15,7 +15,7 @@ we&#x27;ll also be using <code>filamesh</code> and <code>mipgen</code>.</p>
<p>Filament does not have an asset loading system, but it does provide a binary mesh format
called <code>filamesh</code> for simple use cases. Let&#x27;s create a compressed filamesh file for suzanne by
converting <a href="https://github.com/google/filament/blob/main/assets/models/monkey/monkey.obj">this OBJ file</a>:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>filamesh --compress monkey.obj suzanne.filamesh
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>filamesh<span style="color: #bbbbbb"> </span>--compress<span style="color: #bbbbbb"> </span>monkey.obj<span style="color: #bbbbbb"> </span>suzanne.filamesh
</pre></div>
<h2>Create mipmapped textures</h2>
@@ -23,26 +23,26 @@ converting <a href="https://github.com/google/filament/blob/main/assets/models/m
non-compressed variants for each texture, since not all platforms support the same compression
formats. First copy over the PNG files from the <a href="https://github.com/google/filament/blob/main/assets/models/monkey">monkey folder</a>, then do:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># Create mipmaps for base color and two compressed variants.</span>
mipgen albedo.png albedo.ktx
mipgen --compression<span style="color: #666666">=</span>astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
mipgen --compression<span style="color: #666666">=</span>s3tc_rgb_dxt1 albedo.png albedo_s3tc_srgb.ktx
mipgen<span style="color: #bbbbbb"> </span>albedo.png<span style="color: #bbbbbb"> </span>albedo.ktx
mipgen<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>astc_fast_ldr_4x4<span style="color: #bbbbbb"> </span>albedo.png<span style="color: #bbbbbb"> </span>albedo_astc.ktx
mipgen<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>s3tc_rgb_dxt1<span style="color: #bbbbbb"> </span>albedo.png<span style="color: #bbbbbb"> </span>albedo_s3tc_srgb.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the normal map and a compressed variant.</span>
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear normal.png normal.ktx
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear --compression<span style="color: #666666">=</span>etc_rgb8_normalxyz_40 <span style="color: #AA5D1F; font-weight: bold">\</span>
normal.png normal_etc.ktx
mipgen<span style="color: #bbbbbb"> </span>--strip-alpha<span style="color: #bbbbbb"> </span>--kernel<span style="color: #666666">=</span>NORMALS<span style="color: #bbbbbb"> </span>--linear<span style="color: #bbbbbb"> </span>normal.png<span style="color: #bbbbbb"> </span>normal.ktx
mipgen<span style="color: #bbbbbb"> </span>--strip-alpha<span style="color: #bbbbbb"> </span>--kernel<span style="color: #666666">=</span>NORMALS<span style="color: #bbbbbb"> </span>--linear<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>etc_rgb8_normalxyz_40<span style="color: #bbbbbb"> </span><span style="color: #AA5D1F; font-weight: bold">\</span>
<span style="color: #bbbbbb"> </span>normal.png<span style="color: #bbbbbb"> </span>normal_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component roughness map and a compressed variant.</span>
mipgen --grayscale roughness.png roughness.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 roughness.png roughness_etc.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>roughness.png<span style="color: #bbbbbb"> </span>roughness.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>etc_r11_numeric_40<span style="color: #bbbbbb"> </span>roughness.png<span style="color: #bbbbbb"> </span>roughness_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component metallic map and a compressed variant.</span>
mipgen --grayscale metallic.png metallic.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 metallic.png metallic_etc.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>metallic.png<span style="color: #bbbbbb"> </span>metallic.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>etc_r11_numeric_40<span style="color: #bbbbbb"> </span>metallic.png<span style="color: #bbbbbb"> </span>metallic_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component occlusion map and a compressed variant.</span>
mipgen --grayscale ao.png ao.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 ao.png ao_etc.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>ao.png<span style="color: #bbbbbb"> </span>ao.ktx
mipgen<span style="color: #bbbbbb"> </span>--grayscale<span style="color: #bbbbbb"> </span>--compression<span style="color: #666666">=</span>etc_r11_numeric_40<span style="color: #bbbbbb"> </span>ao.png<span style="color: #bbbbbb"> </span>ao_etc.ktx
</pre></div>
<p>For more information on mipgen&#x27;s arguments and supported formats, do <code>mipgen --help</code>.</p>
@@ -50,12 +50,12 @@ mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_num
<h2>Bake environment map</h2>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a> we need to use Filament&#x27;s <code>cmgen</code> tool to produce cubemap files.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/venetian_crossroads_2k.hdr">venetian_crossroads_2k.hdr</a>, then invoke the following commands in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=64</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
<span style="color: #008000">cd</span> venetian* ; mv venetian*_ibl.ktx venetian_crossroads_2k_skybox_tiny.ktx ; <span style="color: #008000">cd</span> -
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen<span style="color: #bbbbbb"> </span>-x<span style="color: #bbbbbb"> </span>.<span style="color: #bbbbbb"> </span>--format<span style="color: #666666">=</span>ktx<span style="color: #bbbbbb"> </span>--size<span style="color: #666666">=64</span><span style="color: #bbbbbb"> </span>--extract-blur<span style="color: #666666">=0</span>.1<span style="color: #bbbbbb"> </span>venetian_crossroads_2k.hdr
<span style="color: #008000">cd</span><span style="color: #bbbbbb"> </span>venetian*<span style="color: #bbbbbb"> </span>;<span style="color: #bbbbbb"> </span>mv<span style="color: #bbbbbb"> </span>venetian*_ibl.ktx<span style="color: #bbbbbb"> </span>venetian_crossroads_2k_skybox_tiny.ktx<span style="color: #bbbbbb"> </span>;<span style="color: #bbbbbb"> </span><span style="color: #008000">cd</span><span style="color: #bbbbbb"> </span>-
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen<span style="color: #bbbbbb"> </span>-x<span style="color: #bbbbbb"> </span>.<span style="color: #bbbbbb"> </span>--format<span style="color: #666666">=</span>ktx<span style="color: #bbbbbb"> </span>--size<span style="color: #666666">=256</span><span style="color: #bbbbbb"> </span>--extract-blur<span style="color: #666666">=0</span>.1<span style="color: #bbbbbb"> </span>venetian_crossroads_2k.hdr
cmgen<span style="color: #bbbbbb"> </span>-x<span style="color: #bbbbbb"> </span>.<span style="color: #bbbbbb"> </span>--format<span style="color: #666666">=</span>ktx<span style="color: #bbbbbb"> </span>--size<span style="color: #666666">=256</span><span style="color: #bbbbbb"> </span>--extract-blur<span style="color: #666666">=0</span>.1<span style="color: #bbbbbb"> </span>venetian_crossroads_2k.hdr
cmgen<span style="color: #bbbbbb"> </span>-x<span style="color: #bbbbbb"> </span>.<span style="color: #bbbbbb"> </span>--format<span style="color: #666666">=</span>ktx<span style="color: #bbbbbb"> </span>--size<span style="color: #666666">=256</span><span style="color: #bbbbbb"> </span>--extract-blur<span style="color: #666666">=0</span>.1<span style="color: #bbbbbb"> </span>venetian_crossroads_2k.hdr
</pre></div>
<h2>Define textured material</h2>
@@ -91,7 +91,7 @@ fragment {
</pre></div>
<p>Next, invoke <code>matc</code> as follows.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o textured.filamat textured.mat
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc<span style="color: #bbbbbb"> </span>-a<span style="color: #bbbbbb"> </span>opengl<span style="color: #bbbbbb"> </span>-p<span style="color: #bbbbbb"> </span>mobile<span style="color: #bbbbbb"> </span>-o<span style="color: #bbbbbb"> </span>textured.filamat<span style="color: #bbbbbb"> </span>textured.mat
</pre></div>
<p>You should now have a material archive in your working directory. For the suzanne asset, the normal
@@ -101,62 +101,62 @@ materials, consult the official document describing the <a href="https://google.
<p>Create a text file called <code>suzanne.html</code> and copy over the HTML that we used in the <a href="tutorial_redball.html">previous
tutorial</a>. Change the last script tag from <code>redball.js</code> to <code>suzanne.js</code>. Next, create <code>suzanne.js</code>
with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>filamesh_url,<span style="color: #bbbbbb"> </span>sky_small_url,<span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>]);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>filamesh_url,<span style="color: #bbbbbb"> </span>sky_small_url,<span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>]);
});
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.suzanne<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>filamesh.renderable;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.suzanne<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>filamesh.renderable;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createView();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov,<span style="color: #bbbbbb"> </span>fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1</span><span style="color: #bbbbbb"> </span><span style="color: #666666">?</span><span style="color: #bbbbbb"> </span>Fov.HORIZONTAL<span style="color: #bbbbbb"> </span><span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>Fov.VERTICAL;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>fov);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov,<span style="color: #bbbbbb"> </span>fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1</span><span style="color: #bbbbbb"> </span><span style="color: #666666">?</span><span style="color: #bbbbbb"> </span>Fov.HORIZONTAL<span style="color: #bbbbbb"> </span><span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>Fov.VERTICAL;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>fov);
<span style="color: #bbbbbb"> </span>}
}
</pre></div>
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We&#x27;ll download
@@ -172,29 +172,29 @@ string -- which might be empty.</p>
<p>In our case, we know that our web server will have <code>astc</code> and <code>s3tc</code> variants for albedo, and <code>etc</code>
variants for the other textures. The uncompressed variants (empty string) are always available as a
last resort. Go ahead and replace the <strong>declare asset URLs</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>texture_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>texture_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_small_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_large_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;textured.filamat&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_small_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_large_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;textured.filamat&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;
</pre></div>
<h2>Create skybox and IBL</h2>
<p>Next, let&#x27;s create the low-resolution skybox and IBL in the <code>App</code> constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_small_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">100000</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_small_url);
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx1(ibl_url);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">100000</span>);
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);
</pre></div>
<p>This allows users to see a reasonable background fairly quickly, before larger assets have finished
@@ -206,31 +206,31 @@ when the assets have finished downloading.</p>
<p>In our callback, we&#x27;ll make several <code>setTextureParameter</code> calls on the material instance, then we&#x27;ll
recreate the skybox using a higher-resolution texture. As a last step we unhide the renderable that
was created in the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #bbbbbb"> </span>albedo_url,<span style="color: #bbbbbb"> </span>roughness_url,<span style="color: #bbbbbb"> </span>metallic_url,<span style="color: #bbbbbb"> </span>normal_url,<span style="color: #bbbbbb"> </span>ao_url],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(roughness_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(metallic_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(normal_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(ao_url);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #bbbbbb"> </span>albedo_url,<span style="color: #bbbbbb"> </span>roughness_url,<span style="color: #bbbbbb"> </span>metallic_url,<span style="color: #bbbbbb"> </span>normal_url,<span style="color: #bbbbbb"> </span>ao_url],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(roughness_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(metallic_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(normal_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(ao_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sampler<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.TextureSampler(<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MagFilter.LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.WrapMode.CLAMP_TO_EDGE);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sampler<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.TextureSampler(
<span style="color: #bbbbbb"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
<span style="color: #bbbbbb"> </span>Filament.MagFilter.LINEAR,
<span style="color: #bbbbbb"> </span>Filament.WrapMode.CLAMP_TO_EDGE);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>,<span style="color: #bbbbbb"> </span>albedo,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span>roughness,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>,<span style="color: #bbbbbb"> </span>metallic,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>,<span style="color: #bbbbbb"> </span>normal,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>,<span style="color: #bbbbbb"> </span>ao,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>,<span style="color: #bbbbbb"> </span>albedo,<span style="color: #bbbbbb"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span>roughness,<span style="color: #bbbbbb"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>,<span style="color: #bbbbbb"> </span>metallic,<span style="color: #bbbbbb"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>,<span style="color: #bbbbbb"> </span>normal,<span style="color: #bbbbbb"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>,<span style="color: #bbbbbb"> </span>ao,<span style="color: #bbbbbb"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_large_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_large_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
});
</pre></div>
<h2>Introduce trackball rotation</h2>
@@ -241,13 +241,13 @@ listens for drag events and computes a rotation matrix.</p>
<p>Next, replace the <strong>initialize gltumble</strong> and <strong>apply gltumble matrix</strong> comments with the following
two code snippets.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Trackball(canvas,<span style="color: #bbbbbb"> </span>{startSpin<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.035</span>});<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Trackball(canvas,<span style="color: #bbbbbb"> </span>{startSpin<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.035</span>});
</pre></div>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();
</pre></div>
<p>That&#x27;s it, we now have a fast-loading interactive demo. The complete JavaScript file is available
+87 -87
View File
@@ -22,9 +22,9 @@ a mobile-friendly page with a full-screen canvas.</p>
&lt;<span style="color: #008000; font-weight: bold">title</span>&gt;Filament Tutorial&lt;/<span style="color: #008000; font-weight: bold">title</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">charset</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;utf-8&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">name</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;viewport&quot;</span> <span style="color: #687822">content</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;width=device-width,user-scalable=no,initial-scale=1&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">style</span>&gt;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">body</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">margin</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">overflow</span>:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">hidden</span>;<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">canvas</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>touch-action:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">none</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">width</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">height</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
&lt;<span style="color: #008000; font-weight: bold">style</span>&gt;
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">body</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">margin</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">overflow</span>:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">hidden</span>;<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">canvas</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>touch-action:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">none</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">width</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">height</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>&lt;/<span style="color: #008000; font-weight: bold">style</span>&gt;
&lt;/<span style="color: #008000; font-weight: bold">head</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">body</span>&gt;
@@ -48,24 +48,24 @@ a mobile-friendly page with a full-screen canvas.</p>
<li><code>triangle.js</code> will contain your application code.</li>
</ul>
<p>Go ahead and create <code>triangle.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span>
<span style="color: #bbbbbb"> </span>}
}
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App()<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App()<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>);
</pre></div>
<p>The two calls to <code>bind()</code> allow us to pass instance methods as callbacks for animation and resize
@@ -81,9 +81,9 @@ that define a PBR material. We&#x27;ll learn more about material packages in the
<h2>Spawn a local server</h2>
<p>Because of CORS restrictions, your web app cannot fetch the material package directly from the
file system. One way around this is to create a temporary server using Python or node:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>python3 -m http.server <span style="color: #3D7B7B; font-style: italic"># Python 3</span>
python -m SimpleHTTPServer <span style="color: #3D7B7B; font-style: italic"># Python 2.7</span>
npx http-server -p <span style="color: #666666">8000</span> <span style="color: #3D7B7B; font-style: italic"># nodejs</span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>python3<span style="color: #bbbbbb"> </span>-m<span style="color: #bbbbbb"> </span>http.server<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"># Python 3</span>
python<span style="color: #bbbbbb"> </span>-m<span style="color: #bbbbbb"> </span>SimpleHTTPServer<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"># Python 2.7</span>
npx<span style="color: #bbbbbb"> </span>http-server<span style="color: #bbbbbb"> </span>-p<span style="color: #bbbbbb"> </span><span style="color: #666666">8000</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"># nodejs</span>
</pre></div>
<p>To see if this works, navigate to <a href="http://localhost:8000">http://localhost:8000</a> and check if you
@@ -93,8 +93,8 @@ with the correct MIME type.</p>
<h2>Create the Engine and Scene</h2>
<p>We now have a basic skeleton that can respond to paint and resize events. Let&#x27;s start adding
Filament objects to the app. Insert the following code into the top of the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);
</pre></div>
<p>The above snippet creates the <code>Engine</code> by passing it a canvas DOM object. The engine needs the
@@ -102,9 +102,9 @@ canvas in order to create a WebGL 2.0 context in its contructor.</p>
<p>The engine is a factory for many Filament entities, including <code>Scene</code>, which is a flat container of
entities. Let&#x27;s go ahead and create a scene, then add a blank entity called <code>triangle</code> into the
scene.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.triangle<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();
<span style="color: #008000; font-weight: bold">this</span>.triangle<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Filament uses an <a href="//en.wikipedia.org/wiki/Entity-component-system">Entity-Component System</a>.
@@ -114,28 +114,28 @@ calls.</p>
<h2>Construct typed arrays</h2>
<p>Next we&#x27;ll create two typed arrays: a positions array with XY coordinates for each vertex, and a
colors array with a 32-bit word for each vertex.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Float32Array</span>([<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Float32Array</span>([
<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),
]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_COLORS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff00ff00</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff0000ff</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_COLORS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff00ff00</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff0000ff</span>]);
</pre></div>
<p>Next we&#x27;ll use the positions and colors buffers to create a single <code>VertexBuffer</code> object.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT2,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">8</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.COLOR,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.UBYTE4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.COLOR)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;
<span style="color: #008000; font-weight: bold">this</span>.vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()
<span style="color: #bbbbbb"> </span>.vertexCount(<span style="color: #666666">3</span>)
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT2,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">8</span>)
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.COLOR,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.UBYTE4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>)
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.COLOR)
<span style="color: #bbbbbb"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_COLORS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_COLORS);
</pre></div>
<p>The above snippet first creates aliases for two enum types, then constructs the vertex buffer using
@@ -149,12 +149,12 @@ attribute. Alternatively, we could have interleaved or concatenated these attrib
buffer slot.</p>
<p>Next we&#x27;ll construct an index buffer. The index buffer for our triangle is trivial: it simply holds
the integers 0,1,2.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(Filament.IndexBuffer$IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()
<span style="color: #bbbbbb"> </span>.indexCount(<span style="color: #666666">3</span>)
<span style="color: #bbbbbb"> </span>.bufferType(Filament.IndexBuffer$IndexType.USHORT)
<span style="color: #bbbbbb"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine,<span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">2</span>]));<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine,<span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">2</span>]));
</pre></div>
<p>Note that constructing an index buffer is similar to constructing a vertex buffer, but it only has
@@ -167,29 +167,29 @@ parameters, and they can be bound to renderables. We&#x27;ll learn more about ma
next tutorial.</p>
<p>After extracting the material instance, we can finally create a renderable component for the
triangle by setting up a bounding box and passing in the vertex and index buffers.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>mat<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat.getDefaultInstance();<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinst)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.vb,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>mat<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat.getDefaultInstance();
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinst)
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.vb,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.ib)
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Next let&#x27;s wrap up the initialization routine by creating the swap chain, renderer, camera, and
view.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();
<span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();
<span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.2</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>],<span style="color: #bbbbbb"> </span>clear<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.2</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>],<span style="color: #bbbbbb"> </span>clear<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span>
<span style="color: #008000; font-weight: bold">this</span>.resize();
</pre></div>
<p>At this point, we&#x27;re done creating all Filament entities, and the code should run without errors.
@@ -197,24 +197,24 @@ However the canvas is still blank!</p>
<h2>Render and resize handlers</h2>
<p>Recall that our App class has a skeletal render method, which the browser calls every time it needs
to repaint. Often this is 60 times a second.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
</pre></div>
<p>Let&#x27;s flesh this out by rotating the triangle and invoking the Filament renderer. Add the following
code to the top of the render method.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>transform<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat4.fromRotation(mat4.create(),<span style="color: #bbbbbb"> </span>radians,<span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span>transform);<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1000</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>transform<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat4.fromRotation(mat4.create(),<span style="color: #bbbbbb"> </span>radians,<span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span>transform);
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
</pre></div>
<p>The first half of our render method obtains the transform component of the triangle entity and uses
@@ -225,14 +225,14 @@ that it wants to skip a frame, hence the <code>if</code> statement.</p>
<p>One last step. Add the following code to the resize method. This adjusts the resolution of the
rendering surface when the window size changes, taking <code>devicePixelRatio</code> into account for high-DPI
displays. It also adjusts the camera frustum accordingly.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Projection<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Projection;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO,<span style="color: #bbbbbb"> </span><span style="color: #666666">-</span>aspect,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Projection<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Projection;
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO,<span style="color: #bbbbbb"> </span><span style="color: #666666">-</span>aspect,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>);
</pre></div>
<p>You should now have a spinning triangle! The completed JavaScript is available
+1 -1
View File
@@ -50,7 +50,6 @@ set(PUBLIC_HDRS
set(SRCS
src/AtlasAllocator.cpp
src/Box.cpp
src/BufferObject.cpp
src/Camera.cpp
src/Color.cpp
@@ -589,6 +588,7 @@ else()
-Wgnu-conditional-omitted-operand
-Wweak-vtables -Wnon-virtual-dtor -Wclass-varargs -Wimplicit-fallthrough
-Wover-aligned
-Werror
)
endif()
+11 -4
View File
@@ -114,7 +114,7 @@ endif()
# ==================================================================================================
if (FILAMENT_SUPPORTS_METAL)
set(METAL_SRCS
set(METAL_OBJC_SRCS
src/metal/MetalBlitter.mm
src/metal/MetalBuffer.mm
src/metal/MetalBufferPool.mm
@@ -124,18 +124,21 @@ if (FILAMENT_SUPPORTS_METAL)
src/metal/MetalExternalImage.mm
src/metal/MetalHandles.mm
src/metal/MetalPlatform.mm
src/metal/MetalResourceTracker.cpp
src/metal/MetalState.mm
src/metal/MetalTimerQuery.mm
src/metal/MetalUtils.mm
)
list(APPEND SRCS ${METAL_SRCS})
set(METAL_CPP_SRCS
src/metal/MetalResourceTracker.cpp
)
list(APPEND SRCS ${METAL_OBJC_SRCS} ${METAL_CPP_SRCS})
if (IOS)
# Objective-C++ sources need an additional compiler flag on iOS to disable exceptions.
set_property(SOURCE
${METAL_SRCS}
${METAL_OBJC_SRCS}
src/opengl/platforms/PlatformCocoaTouchGL.mm
src/opengl/platforms/CocoaTouchExternalImage.mm
src/opengl/platforms/PlatformCocoaGL.mm
@@ -176,12 +179,15 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPlatform.cpp
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp
src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanTaskHandler.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanUtility.cpp
@@ -350,6 +356,7 @@ else()
-Wgnu-conditional-omitted-operand
-Wweak-vtables -Wnon-virtual-dtor -Wclass-varargs -Wimplicit-fallthrough
-Wover-aligned
-Werror
)
endif()
@@ -42,11 +42,42 @@
*/
namespace filament::backend {
/**
* Requests a SwapChain with an alpha channel.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the swap chain may be used as a source surface
* for reading back render results. This config flag must be set when creating
* any SwapChain that will be used as the source for a blit operation.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the native window is a CVPixelBufferRef.
*
* This is only supported by the Metal backend. The CVPixelBuffer must be in the
* kCVPixelFormatType_32BGRA format.
*
* It is not necessary to add an additional retain call before passing the pixel buffer to
* Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and
* CVPixelBufferRelease when the swap chain is destroyed.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
/**
* Indicates that the SwapChain must automatically perform linear to srgb encoding.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_SRGB_COLORSPACE = 0x10;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
@@ -279,8 +279,8 @@ public:
break;
}
size_t bpr = bpp * stride;
size_t bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
size_t const bpr = bpp * stride;
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height;
}
@@ -66,7 +66,15 @@ public:
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t& flags) noexcept = 0;
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept = 0;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Called by the driver create a headless SwapChain.
@@ -79,7 +87,7 @@ public:
* TODO: we need a more generic way of passing construction parameters
* A void* might be enough.
*/
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept = 0;
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
/**
* Called by the driver to destroys the SwapChain
@@ -52,8 +52,8 @@ protected:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -47,8 +47,8 @@ public:
uint32_t createDefaultRenderTarget() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -58,8 +58,9 @@ protected:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -97,15 +98,21 @@ protected:
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
EGLConfig mEGLTransparentConfig = EGL_NO_CONFIG_KHR;
// supported extensions detected at runtime
struct {
bool OES_EGL_image_external_essl3 = false;
struct {
bool OES_EGL_image_external_essl3 = false;
} gl;
struct {
bool KHR_no_config_context = false;
bool KHR_gl_colorspace = false;
} egl;
} ext;
private:
void initializeGlExtensions() noexcept;
EGLConfig findSwapChainConfig(uint64_t flags) const;
};
} // namespace filament::backend
@@ -48,8 +48,8 @@ protected:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -46,8 +46,8 @@ protected:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -43,8 +43,8 @@ protected:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -27,8 +27,19 @@ namespace filament::backend {
class VulkanPlatform : public Platform {
public:
struct SurfaceBundle {
void* surface;
// On certain platforms, the extent of the surface cannot be queried from Vulkan. In those
// situations, we allow the frontend to pass in the extent to use in creating the swap
// chains. Platform implementation should set extent to 0 if they do not expect to set the
// swap chain extent.
uint32_t width;
uint32_t height;
};
// Given a Vulkan instance and native window handle, creates the platform-specific surface.
virtual void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept = 0;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept = 0;
~VulkanPlatform() override;
};
@@ -299,6 +299,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, canGenerateMipmaps)
+1
View File
@@ -75,6 +75,7 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
void CommandStream::execute(void* buffer) {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
Profiler profiler;
@@ -706,6 +706,11 @@ bool MetalDriver::isAutoDepthResolveSupported() {
return mContext->supportsAutoDepthResolve;
}
bool MetalDriver::isSRGBSwapChainSupported() {
// the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag is not supported
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1282,6 +1287,7 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
fromRegion:srcRegion
mipmapLevel:0];
scheduleDestroy(std::move(*p));
delete p;
}];
}
+3 -1
View File
@@ -391,7 +391,9 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
function.label = @(program.getName().c_str());
if (!program.getName().empty()) {
function.label = @(program.getName().c_str());
}
assert_invariant(function);
*shaderFunctions[i] = function;
}
+5 -1
View File
@@ -173,7 +173,11 @@ bool NoopDriver::isAutoDepthResolveSupported() {
return true;
}
bool NoopDriver::isWorkaroundNeeded(Workaround workaround) {
bool NoopDriver::isSRGBSwapChainSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
+6 -1
View File
@@ -120,7 +120,12 @@ constexpr inline GLenum getBufferBindingType(BufferObjectBinding bindingType) no
case BufferObjectBinding::UNIFORM:
return GL_UNIFORM_BUFFER;
case BufferObjectBinding::SHADER_STORAGE:
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
return GL_SHADER_STORAGE_BUFFER;
#else
utils::panic(__func__, __FILE__, __LINE__, "SHADER_STORAGE not supported");
return 0x90D2; // just to return something
#endif
}
}
@@ -418,7 +423,7 @@ constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
case TextureFormat::RGBA32I: return GL_RGBA32I;
// compressed formats
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_4_3) || defined(GL_ARB_ES3_compatibility)
#if defined(GL_ES_VERSION_3_0) || defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_ARB_ES3_compatibility)
case TextureFormat::EAC_R11: return GL_COMPRESSED_R11_EAC;
case TextureFormat::EAC_R11_SIGNED: return GL_COMPRESSED_SIGNED_R11_EAC;
case TextureFormat::EAC_RG11: return GL_COMPRESSED_RG11_EAC;
+118 -75
View File
@@ -16,6 +16,10 @@
#include "OpenGLContext.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <utility>
// change to true to display all GL extensions in the console on start-up
#define DEBUG_PRINT_EXTENSIONS false
@@ -24,17 +28,17 @@ using namespace utils;
namespace filament::backend {
bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
char const* version = (char const*)glGetString(GL_VERSION);
// This works on all versions of GLES
int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
return n == 2;
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// OpenGL version
glGetIntegerv(GL_MAJOR_VERSION, major);
glGetIntegerv(GL_MINOR_VERSION, minor);
return (glGetError() == GL_NO_ERROR);
}
#ifdef BACKEND_OPENGL_VERSION_GLES
char const* version = (char const*)glGetString(GL_VERSION);
// This works on all versions of GLES
int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
return n == 2;
#else
// OpenGL version
glGetIntegerv(GL_MAJOR_VERSION, major);
glGetIntegerv(GL_MINOR_VERSION, minor);
return (glGetError() == GL_NO_ERROR);
#endif
}
OpenGLContext::OpenGLContext() noexcept {
@@ -69,47 +73,48 @@ OpenGLContext::OpenGLContext() noexcept {
constexpr GLint MAX_VERTEX_SAMPLER_COUNT = caps3.MAX_VERTEX_SAMPLER_COUNT;
constexpr GLint MAX_FRAGMENT_SAMPLER_COUNT = caps3.MAX_FRAGMENT_SAMPLER_COUNT;
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
initExtensionsGLES();
if (state.major == 3) {
assert_invariant(gets.max_texture_image_units >= 16);
assert_invariant(gets.max_combined_texture_image_units >= 32);
if (state.minor >= 1) {
features.multisample_texture = true;
// figure out our feature level
if (ext.EXT_texture_cube_map_array) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
}
}
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// OpenGL version
initExtensionsGL();
if (state.major == 4) {
assert_invariant(state.minor >= 1);
mShaderModel = ShaderModel::DESKTOP;
if (state.minor >= 3) {
// cubemap arrays are available as of OpenGL 4.0
#ifdef BACKEND_OPENGL_VERSION_GLES
initExtensionsGLES();
if (state.major == 3) {
// Runtime OpenGL version is ES 3.x
assert_invariant(gets.max_texture_image_units >= 16);
assert_invariant(gets.max_combined_texture_image_units >= 32);
if (state.minor >= 1) {
features.multisample_texture = true;
// figure out our feature level
if (ext.EXT_texture_cube_map_array) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
// figure out our feature level
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
features.multisample_texture = true;
}
// feedback loops are allowed on GL desktop as long as writes are disabled
bugs.allow_read_only_ancillary_feedback_loop = true;
assert_invariant(gets.max_texture_image_units >= 16);
assert_invariant(gets.max_combined_texture_image_units >= 32);
}
#else
initExtensionsGL();
if (state.major == 4) {
assert_invariant(state.minor >= 1);
mShaderModel = ShaderModel::DESKTOP;
if (state.minor >= 3) {
// cubemap arrays are available as of OpenGL 4.0
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
// figure out our feature level
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
features.multisample_texture = true;
}
// feedback loops are allowed on GL desktop as long as writes are disabled
bugs.allow_read_only_ancillary_feedback_loop = true;
assert_invariant(gets.max_texture_image_units >= 16);
assert_invariant(gets.max_combined_texture_image_units >= 32);
#endif
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
@@ -330,7 +335,7 @@ void OpenGLContext::setDefaultState() noexcept {
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
#ifdef BACKEND_OPENGL_VERSION_GL
glEnable(GL_PROGRAM_POINT_SIZE);
enable(GL_PROGRAM_POINT_SIZE);
#endif
@@ -344,13 +349,17 @@ void OpenGLContext::setDefaultState() noexcept {
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if defined(GL_EXT_clip_control) || defined(GL_ARB_clip_control) || defined(GL_VERSION_4_5)
if (ext.EXT_clip_control) {
#if defined(BACKEND_OPENGL_VERSION_GL)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
#elif defined(GL_EXT_clip_control)
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
#endif
}
}
#ifdef BACKEND_OPENGL_VERSION_GLES
void OpenGLContext::initExtensionsGLES() noexcept {
const char * const extensions = (const char*)glGetString(GL_EXTENSIONS);
GLUtils::unordered_string_set const exts = GLUtils::split(extensions);
@@ -385,7 +394,6 @@ void OpenGLContext::initExtensionsGLES() noexcept {
ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv);
ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv);
ext.OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv);
ext.QCOM_tiled_rendering = exts.has("GL_QCOM_tiled_rendering"sv);
ext.WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
ext.WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
ext.WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
@@ -395,6 +403,10 @@ void OpenGLContext::initExtensionsGLES() noexcept {
}
}
#endif // BACKEND_OPENGL_VERSION_GLES
#ifdef BACKEND_OPENGL_VERSION_GL
void OpenGLContext::initExtensionsGL() noexcept {
GLUtils::unordered_string_set exts;
GLint n = 0;
@@ -414,25 +426,34 @@ void OpenGLContext::initExtensionsGL() noexcept {
auto minor = state.minor;
ext.APPLE_color_buffer_packed_float = true; // Assumes core profile.
ext.ARB_shading_language_packing = exts.has("GL_ARB_shading_language_packing"sv) || (major == 4 && minor >= 2);
ext.EXT_clip_control = exts.has("GL_ARB_clip_control"sv) || (major == 4 && minor >= 5);
ext.EXT_clip_control = (major == 4 && minor >= 5);
ext.EXT_color_buffer_float = true; // Assumes core profile.
ext.EXT_color_buffer_half_float = true; // Assumes core profile.
ext.EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext.EXT_disjoint_timer_query = true;
ext.EXT_multisampled_render_to_texture = false;
ext.EXT_multisampled_render_to_texture2 = false;
ext.EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
ext.EXT_texture_compression_etc2 = exts.has("GL_ARB_ES3_compatibility"sv);
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
ext.EXT_texture_cube_map_array = true;
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext.EXT_texture_sRGB = exts.has("GL_EXT_texture_sRGB"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
ext.KHR_debug = major >= 4 && minor >= 3;
ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv);
ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv);
ext.OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv);
ext.OES_EGL_image_external_essl3 = false;
ext.WEBGL_compressed_texture_etc = false;
ext.WEBGL_compressed_texture_s3tc = false;
ext.WEBGL_compressed_texture_s3tc_srgb = false;
}
#endif // BACKEND_OPENGL_VERSION_GL
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
if (target == GL_ELEMENT_ARRAY_BUFFER) {
constexpr size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
@@ -465,13 +486,8 @@ void OpenGLContext::pixelStore(GLenum pname, GLint param) noexcept {
switch (pname) {
case GL_PACK_ALIGNMENT: pcur = &state.pack.alignment; break;
case GL_PACK_ROW_LENGTH: pcur = &state.pack.row_length; break;
case GL_PACK_SKIP_PIXELS: pcur = &state.pack.skip_pixels; break; // convenience
case GL_PACK_SKIP_ROWS: pcur = &state.pack.skip_row; break; // convenience
case GL_UNPACK_ALIGNMENT: pcur = &state.unpack.alignment; break;
case GL_UNPACK_ROW_LENGTH: pcur = &state.unpack.row_length; break;
case GL_UNPACK_SKIP_PIXELS: pcur = &state.unpack.skip_pixels; break; // convenience
case GL_UNPACK_SKIP_ROWS: pcur = &state.unpack.skip_row; break; // convenience
default:
goto default_case;
}
@@ -618,12 +634,10 @@ void OpenGLContext::resetState() noexcept {
GLenum const bufferTargets[] = {
GL_UNIFORM_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
#if !defined(__EMSCRIPTEN__)
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
GL_SHADER_STORAGE_BUFFER,
#endif
GL_ARRAY_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
@@ -646,23 +660,30 @@ void OpenGLContext::resetState() noexcept {
// Reset state.textures to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.textures = {};
const GLuint textureTargets[] = {
GL_TEXTURE_2D,
GL_TEXTURE_2D_ARRAY,
GL_TEXTURE_CUBE_MAP,
GL_TEXTURE_3D,
const std::pair<GLuint, bool> textureTargets[] = {
{ GL_TEXTURE_2D, true },
{ GL_TEXTURE_2D_ARRAY, true },
{ GL_TEXTURE_CUBE_MAP, true },
{ GL_TEXTURE_3D, true },
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
{ GL_TEXTURE_2D_MULTISAMPLE, true },
#endif
#if !defined(__EMSCRIPTEN__)
GL_TEXTURE_2D_MULTISAMPLE,
GL_TEXTURE_EXTERNAL_OES,
GL_TEXTURE_CUBE_MAP_ARRAY,
#if defined(GL_OES_EGL_image_external)
{ GL_TEXTURE_EXTERNAL_OES, ext.OES_EGL_image_external_essl3 },
#endif
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_texture_cube_map_array)
{ GL_TEXTURE_CUBE_MAP_ARRAY, ext.EXT_texture_cube_map_array },
#endif
#endif
};
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindSampler(unit, 0);
for (auto const target : textureTargets) {
glBindTexture(target, 0);
for (auto [target, available] : textureTargets) {
if (available) {
glBindTexture(target, 0);
}
}
}
glActiveTexture(GL_TEXTURE0 + state.textures.active);
@@ -670,14 +691,10 @@ void OpenGLContext::resetState() noexcept {
// state.unpack
glPixelStorei(GL_UNPACK_ALIGNMENT, state.unpack.alignment);
glPixelStorei(GL_UNPACK_ROW_LENGTH, state.unpack.row_length);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, state.unpack.skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_ROWS, state.unpack.skip_row);
// state.pack
glPixelStorei(GL_PACK_ALIGNMENT, state.pack.alignment);
glPixelStorei(GL_PACK_ROW_LENGTH, state.pack.row_length);
glPixelStorei(GL_PACK_SKIP_PIXELS, state.pack.skip_pixels);
glPixelStorei(GL_PACK_SKIP_ROWS, state.pack.skip_row);
glPixelStorei(GL_PACK_ROW_LENGTH, 0); // we rely on GL_PACK_ROW_LENGTH being zero
// state.window
glScissor(
@@ -696,4 +713,30 @@ void OpenGLContext::resetState() noexcept {
}
OpenGLContext::FenceSync OpenGLContext::createFenceSync(
OpenGLPlatform&) noexcept {
auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
CHECK_GL_ERROR(utils::slog.e)
return { .sync = sync };
}
void OpenGLContext::destroyFenceSync(
OpenGLPlatform&, FenceSync sync) noexcept {
glDeleteSync(sync.sync);
CHECK_GL_ERROR(utils::slog.e)
}
OpenGLContext::FenceSync::Status OpenGLContext::clientWaitSync(
OpenGLPlatform&, FenceSync sync) const noexcept {
GLenum const status = glClientWaitSync(sync.sync, 0, 0u);
CHECK_GL_ERROR(utils::slog.e)
using Status = OpenGLContext::FenceSync::Status;
switch (status) {
case GL_ALREADY_SIGNALED: return Status::ALREADY_SIGNALED;
case GL_TIMEOUT_EXPIRED: return Status::TIMEOUT_EXPIRED;
case GL_CONDITION_SATISFIED: return Status::CONDITION_SATISFIED;
default: return Status::FAILURE;
}
}
} // namesapce filament
+74 -77
View File
@@ -33,6 +33,8 @@
namespace filament::backend {
class OpenGLPlatform;
class OpenGLContext {
public:
static constexpr const size_t MAX_TEXTURE_UNIT_COUNT = MAX_SAMPLER_COUNT;
@@ -67,6 +69,24 @@ public:
OpenGLContext() noexcept;
constexpr bool isAtLeastGL(int major, int minor) const noexcept {
#ifdef BACKEND_OPENGL_VERSION_GL
return state.major > major || (state.major == major && state.minor >= minor);
#else
(void)major, (void)minor;
return false;
#endif
}
constexpr bool isAtLeastGLES(int major, int minor) const noexcept {
#ifdef BACKEND_OPENGL_VERSION_GLES
return state.major > major || (state.major == major && state.minor >= minor);
#else
(void)major, (void)minor;
return false;
#endif
}
constexpr static inline size_t getIndexForTextureTarget(GLuint target) noexcept;
constexpr inline size_t getIndexForCap(GLenum cap) noexcept;
constexpr static inline size_t getIndexForBufferTarget(GLenum target) noexcept;
@@ -110,9 +130,6 @@ public:
GLenum sfailBack, GLenum dpfailBack, GLenum dppassBack) noexcept;
inline void stencilMaskSeparate(GLuint maskFront, GLuint maskBack) noexcept;
inline void polygonOffset(GLfloat factor, GLfloat units) noexcept;
inline void beginQuery(GLenum target, GLuint query) noexcept;
inline void endQuery(GLenum target) noexcept;
inline GLuint getQuery(GLenum target) const noexcept;
inline void setScissor(GLint left, GLint bottom, GLsizei width, GLsizei height) noexcept;
inline void viewport(GLint left, GLint bottom, GLsizei width, GLsizei height) noexcept;
@@ -121,6 +138,26 @@ public:
void deleteBuffers(GLsizei n, const GLuint* buffers, GLenum target) noexcept;
void deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept;
// we abstract GL's sync because it's not available in ES2, but we can use EGL's sync
// instead, if available.
struct FenceSync {
enum class Status {
ALREADY_SIGNALED,
TIMEOUT_EXPIRED,
CONDITION_SATISFIED,
FAILURE
};
union {
void* fence;
GLsync sync;
};
};
FenceSync createFenceSync(OpenGLPlatform& platform) noexcept;
void destroyFenceSync(OpenGLPlatform& platform, FenceSync sync) noexcept;
FenceSync::Status clientWaitSync(OpenGLPlatform& platform, FenceSync sync) const noexcept;
// glGet*() values
struct {
GLfloat max_anisotropy;
@@ -149,23 +186,22 @@ public:
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_disjoint_timer_query;
bool EXT_multisampled_render_to_texture;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_shader_framebuffer_fetch;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool EXT_texture_compression_bptc;
bool EXT_texture_compression_etc2;
bool EXT_texture_compression_rgtc;
bool EXT_texture_compression_s3tc;
bool EXT_texture_compression_s3tc_srgb;
bool EXT_texture_compression_rgtc;
bool EXT_texture_compression_bptc;
bool EXT_texture_cube_map_array;
bool EXT_texture_filter_anisotropic;
bool EXT_texture_sRGB;
bool GOOGLE_cpp_style_line_directive;
bool KHR_debug;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_EGL_image_external_essl3;
bool QCOM_tiled_rendering;
bool WEBGL_compressed_texture_etc;
bool WEBGL_compressed_texture_s3tc;
bool WEBGL_compressed_texture_s3tc_srgb;
@@ -315,8 +351,8 @@ public:
GLintptr offset = 0;
GLsizeiptr size = 0;
} buffers[MAX_BUFFER_BINDINGS];
} targets[2]; // there are only 2 indexed buffer target (uniform and transform feedback)
GLuint genericBinding[9] = { 0 };
} targets[3]; // there are only 3 indexed buffer targets
GLuint genericBinding[7] = {};
} buffers;
struct {
@@ -332,15 +368,10 @@ public:
struct {
GLint row_length = 0;
GLint alignment = 4;
GLint skip_pixels = 0;
GLint skip_row = 0;
} unpack;
struct {
GLint row_length = 0;
GLint alignment = 4;
GLint skip_pixels = 0;
GLint skip_row = 0;
} pack;
struct {
@@ -348,10 +379,6 @@ public:
vec4gli viewport { 0 };
vec2glf depthRange { 0.0f, 1.0f };
} window;
struct {
GLuint timer = -1u;
} queries;
} state;
private:
@@ -403,8 +430,12 @@ private:
RenderPrimitive mDefaultVAO;
// this is chosen to minimize code size
#if defined(BACKEND_OPENGL_VERSION_GLES)
void initExtensionsGLES() noexcept;
#endif
#if defined(BACKEND_OPENGL_VERSION_GL)
void initExtensionsGL() noexcept;
#endif
template <typename T, typename F>
static inline void update_state(T& state, T const& expected, F functor, bool force = false) noexcept {
@@ -429,7 +460,9 @@ constexpr size_t OpenGLContext::getIndexForTextureTarget(GLuint target) noexcept
case GL_TEXTURE_2D: return 0;
case GL_TEXTURE_2D_ARRAY: return 1;
case GL_TEXTURE_CUBE_MAP: return 2;
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
case GL_TEXTURE_2D_MULTISAMPLE: return 3;
#endif
case GL_TEXTURE_EXTERNAL_OES: return 4;
case GL_TEXTURE_3D: return 5;
case GL_TEXTURE_CUBE_MAP_ARRAY: return 6;
@@ -450,17 +483,15 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
case GL_SAMPLE_ALPHA_TO_COVERAGE: index = 6; break;
case GL_SAMPLE_COVERAGE: index = 7; break;
case GL_POLYGON_OFFSET_FILL: index = 8; break;
case GL_PRIMITIVE_RESTART_FIXED_INDEX: index = 9; break;
case GL_RASTERIZER_DISCARD: index = 10; break;
#ifdef GL_ARB_seamless_cube_map
case GL_TEXTURE_CUBE_MAP_SEAMLESS: index = 11; break;
case GL_TEXTURE_CUBE_MAP_SEAMLESS: index = 9; break;
#endif
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 12; break;
#ifdef BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 10; break;
#endif
default: index = 13; break; // should never happen
default: break;
}
assert_invariant(index < 13 && index < state.enables.caps.size());
assert_invariant(index < state.enables.caps.size());
return index;
}
@@ -470,15 +501,14 @@ constexpr size_t OpenGLContext::getIndexForBufferTarget(GLenum target) noexcept
// The indexed buffers MUST be first in this list (those usable with bindBufferRange)
case GL_UNIFORM_BUFFER: index = 0; break;
case GL_TRANSFORM_FEEDBACK_BUFFER: index = 1; break;
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
case GL_SHADER_STORAGE_BUFFER: index = 2; break;
#endif
case GL_ARRAY_BUFFER: index = 3; break;
case GL_COPY_READ_BUFFER: index = 4; break;
case GL_COPY_WRITE_BUFFER: index = 5; break;
case GL_ELEMENT_ARRAY_BUFFER: index = 6; break;
case GL_PIXEL_PACK_BUFFER: index = 7; break;
case GL_PIXEL_UNPACK_BUFFER: index = 8; break;
default: index = 9; break; // should never happen
case GL_ELEMENT_ARRAY_BUFFER: index = 4; break;
case GL_PIXEL_PACK_BUFFER: index = 5; break;
case GL_PIXEL_UNPACK_BUFFER: index = 6; break;
default: break;
}
assert_invariant(index < sizeof(state.buffers.genericBinding)/sizeof(state.buffers.genericBinding[0])); // NOLINT(misc-redundant-expression)
return index;
@@ -501,21 +531,21 @@ void OpenGLContext::bindSampler(GLuint unit, GLuint sampler) noexcept {
}
void OpenGLContext::setScissor(GLint left, GLint bottom, GLsizei width, GLsizei height) noexcept {
vec4gli scissor(left, bottom, width, height);
vec4gli const scissor(left, bottom, width, height);
update_state(state.window.scissor, scissor, [&]() {
glScissor(left, bottom, width, height);
});
}
void OpenGLContext::viewport(GLint left, GLint bottom, GLsizei width, GLsizei height) noexcept {
vec4gli viewport(left, bottom, width, height);
vec4gli const viewport(left, bottom, width, height);
update_state(state.window.viewport, viewport, [&]() {
glViewport(left, bottom, width, height);
});
}
void OpenGLContext::depthRange(GLclampf near, GLclampf far) noexcept {
vec2glf depthRange(near, far);
vec2glf const depthRange(near, far);
update_state(state.window.depthRange, depthRange, [&]() {
glDepthRangef(near, far);
});
@@ -526,7 +556,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
update_state(state.vao.p, vao, [&]() {
glBindVertexArray(vao->vao);
// update GL_ELEMENT_ARRAY_BUFFER, which is updated by glBindVertexArray
size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
size_t const targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
state.buffers.genericBinding[targetIndex] = vao->elementArray;
if (UTILS_UNLIKELY(bugs.vao_doesnt_store_element_array_buffer_binding)) {
// This shouldn't be needed, but it looks like some drivers don't do the implicit
@@ -538,8 +568,10 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size) noexcept {
size_t targetIndex = getIndexForBufferTarget(target);
assert_invariant(targetIndex <= 2); // validity check
size_t const targetIndex = getIndexForBufferTarget(target);
// validity check
assert_invariant(targetIndex < sizeof(state.buffers.targets) / sizeof(*state.buffers.targets));
// this ALSO sets the generic binding
if ( state.buffers.targets[targetIndex].buffers[index].name != buffer
@@ -616,7 +648,7 @@ void OpenGLContext::disableVertexAttribArray(GLuint index) noexcept {
}
void OpenGLContext::enable(GLenum cap) noexcept {
size_t index = getIndexForCap(cap);
size_t const index = getIndexForCap(cap);
if (UTILS_UNLIKELY(!state.enables.caps[index])) {
state.enables.caps.set(index);
glEnable(cap);
@@ -624,7 +656,7 @@ void OpenGLContext::enable(GLenum cap) noexcept {
}
void OpenGLContext::disable(GLenum cap) noexcept {
size_t index = getIndexForCap(cap);
size_t const index = getIndexForCap(cap);
if (UTILS_UNLIKELY(state.enables.caps[index])) {
state.enables.caps.unset(index);
glDisable(cap);
@@ -723,41 +755,6 @@ void OpenGLContext::polygonOffset(GLfloat factor, GLfloat units) noexcept {
});
}
void OpenGLContext::beginQuery(GLenum target, GLuint query) noexcept {
switch (target) {
case GL_TIME_ELAPSED:
if (state.queries.timer != -1u) {
// this is an error
break;
}
state.queries.timer = query;
break;
default:
return;
}
glBeginQuery(target, query);
}
void OpenGLContext::endQuery(GLenum target) noexcept {
switch (target) {
case GL_TIME_ELAPSED:
state.queries.timer = -1u;
break;
default:
return;
}
glEndQuery(target);
}
GLuint OpenGLContext::getQuery(GLenum target) const noexcept {
switch (target) {
case GL_TIME_ELAPSED:
return state.queries.timer;
default:
return 0;
}
}
} // namespace filament
#endif //TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
+154 -117
View File
@@ -69,7 +69,9 @@ using namespace utils;
namespace filament::backend {
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* const platform, void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
OpenGLPlatform* const platform,
void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
}
@@ -129,18 +131,18 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
return {};
}
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
if (UTILS_UNLIKELY(!(major >= 3 && minor >= 0))) {
PANIC_LOG("OpenGL ES 3.0 minimum needed (current %d.%d)", major, minor);
goto cleanup;
}
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// we require GL 4.1 headers and minimum version
if (UTILS_UNLIKELY(!((major == 4 && minor >= 1) || major > 4))) {
PANIC_LOG("OpenGL 4.1 minimum needed (current %d.%d)", major, minor);
goto cleanup;
}
#if defined(BACKEND_OPENGL_VERSION_GLES)
if (UTILS_UNLIKELY(!(major >= 3 && minor >= 0))) {
PANIC_LOG("OpenGL ES 3.0 minimum needed (current %d.%d)", major, minor);
goto cleanup;
}
#else
// we require GL 4.1 headers and minimum version
if (UTILS_UNLIKELY(!((major == 4 && minor >= 1) || major > 4))) {
PANIC_LOG("OpenGL 4.1 minimum needed (current %d.%d)", major, minor);
goto cleanup;
}
#endif
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig const validConfig {
@@ -177,8 +179,16 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
#endif
if (mContext.ext.EXT_disjoint_timer_query ||
BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// Timer queries are core in GL 3.3, otherwise we need EXT_disjoint_timer_query
// iOS headers don't define GL_EXT_disjoint_timer_query, so make absolutely sure
// we won't use it.
#if defined(BACKEND_OPENGL_VERSION_GL)
assert_invariant(mContext.ext.EXT_disjoint_timer_query);
#endif
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
if (mContext.ext.EXT_disjoint_timer_query) {
// timer queries are available
if (mContext.bugs.dont_use_timer_query && mPlatform.canCreateFence()) {
// however, they don't work well, revert to using fences if we can.
@@ -187,7 +197,9 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mTimerQueryImpl = new TimerQueryNative(mContext);
}
mFrameTimeSupported = true;
} else if (mPlatform.canCreateFence()) {
} else
#endif
if (mPlatform.canCreateFence()) {
// no timer queries, but we can use fences
mTimerQueryImpl = new OpenGLTimerQueryFence(mPlatform);
mFrameTimeSupported = true;
@@ -537,23 +549,29 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
GLsizei(width), GLsizei(height), GLsizei(depth) * 6);
break;
}
#ifdef BACKEND_OPENGL_LEVEL_GLES31
case GL_TEXTURE_2D_MULTISAMPLE:
if constexpr (TEXTURE_2D_MULTISAMPLE_SUPPORTED) {
// NOTE: if there is a mix of texture and renderbuffers, "fixed_sample_locations" must be true
// NOTE: what's the benefit of setting "fixed_sample_locations" to false?
#if BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES31
// only supported from GL 4.3 and GLES 3.1 headers
glTexStorage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat,
GLsizei(width), GLsizei(height), GL_TRUE);
#elif defined(GL_VERSION_4_1)
// only supported in GL (GL4.1 doesn't support glTexStorage2DMultisample)
glTexImage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat,
GLsizei(width), GLsizei(height), GL_TRUE);
if (mContext.isAtLeastGL(4, 3) || mContext.isAtLeastGLES(3, 1)) {
// only supported from GL 4.3 and GLES 3.1 headers
glTexStorage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat,
GLsizei(width), GLsizei(height), GL_TRUE);
}
#ifdef BACKEND_OPENGL_VERSION_GL
else {
// only supported in GL (GL4.1 doesn't support glTexStorage2DMultisample)
glTexImage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat,
GLsizei(width), GLsizei(height), GL_TRUE);
}
#endif
} else {
PANIC_LOG("GL_TEXTURE_2D_MULTISAMPLE is not supported");
}
break;
#endif // BACKEND_OPENGL_LEVEL_GLES31
default: // cannot happen
break;
}
@@ -621,6 +639,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
if (t->samples > 1) {
// Note: we can't be here in practice because filament's user API doesn't
// allow the creation of multi-sampled textures.
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
@@ -629,6 +648,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
#endif
}
textureStorage(t, w, h, depth);
}
@@ -721,6 +741,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
if (t->samples > 1) {
// Note: we can't be here in practice because filament's user API doesn't
// allow the creation of multi-sampled textures.
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
@@ -728,6 +749,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
#endif
}
CHECK_GL_ERROR(utils::slog.e)
@@ -800,6 +822,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
GLTexture* t = handle_cast<GLTexture*>(binfo.handle);
assert_invariant(t);
assert_invariant(t->target != SamplerType::SAMPLER_EXTERNAL);
assert_invariant(rt->width <= valueForLevel(binfo.level, t->width) &&
rt->height <= valueForLevel(binfo.level, t->height));
@@ -907,7 +930,9 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case GL_TEXTURE_2D:
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
case GL_TEXTURE_2D_MULTISAMPLE:
#endif
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
target, t->gl.id, binfo.level);
@@ -1135,21 +1160,26 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
rt->gl.samples = samples;
rt->targets = targets;
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = { std::numeric_limits<uint32_t>::max() };
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = { 0 };
auto checkDimensions = [&tmin, &tmax](GLTexture* t, uint8_t level) {
const auto twidth = std::max(1u, t->width >> level);
const auto theight = std::max(1u, t->height >> level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
};
if (any(targets & TargetBufferFlags::COLOR_ALL)) {
GLenum bufs[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { GL_NONE };
const size_t maxDrawBuffers = getMaxDrawBuffers();
for (size_t i = 0; i < maxDrawBuffers; i++) {
if (any(targets & getTargetBufferFlagsAt(i))) {
auto t = rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
const auto twidth = std::max(1u, t->width >> color[i].level);
const auto theight = std::max(1u, t->height >> color[i].level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
assert_invariant(color[i].handle);
rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i);
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
checkDimensions(rt->gl.color[i], color[i].level);
}
}
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
@@ -1159,37 +1189,28 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
// handle special cases first (where depth/stencil are packed)
bool specialCased = false;
if ((targets & TargetBufferFlags::DEPTH_AND_STENCIL) == TargetBufferFlags::DEPTH_AND_STENCIL) {
assert_invariant(!stencil.handle || stencil.handle == depth.handle);
auto t = rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
const auto twidth = std::max(1u, t->width >> depth.level);
const auto theight = std::max(1u, t->height >> depth.level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
if (any(rt->gl.depth->usage & TextureUsage::SAMPLEABLE) ||
(!depth.handle && !stencil.handle)) {
// special case: depth & stencil requested, and both provided as the same texture
// special case: depth & stencil requested, but both not provided
specialCased = true;
assert_invariant(depth.handle);
// either we supplied only the depth handle or both depth/stencil are identical and not null
if (depth.handle && (stencil.handle == depth.handle || !stencil.handle)) {
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT);
specialCased = true;
checkDimensions(rt->gl.depth, depth.level);
}
}
if (!specialCased) {
if (any(targets & TargetBufferFlags::DEPTH)) {
auto t = rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
const auto twidth = std::max(1u, t->width >> depth.level);
const auto theight = std::max(1u, t->height >> depth.level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
assert_invariant(depth.handle);
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT);
checkDimensions(rt->gl.depth, depth.level);
}
if (any(targets & TargetBufferFlags::STENCIL)) {
auto t = rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
const auto twidth = std::max(1u, t->width >> stencil.level);
const auto theight = std::max(1u, t->height >> stencil.level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
assert_invariant(stencil.handle);
rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT);
checkDimensions(rt->gl.stencil, stencil.level);
}
}
@@ -1211,19 +1232,19 @@ void OpenGLDriver::createSyncR(Handle<HwSync> fh, int) {
DEBUG_MARKER()
GLSync* f = handle_cast<GLSync *>(fh);
f->gl.sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
CHECK_GL_ERROR(utils::slog.e)
f->handle = mContext.createFenceSync(mPlatform);
// check the status of the sync once a frame, since we must do this from our thread
std::weak_ptr<GLSync::State> const weak = f->result;
runEveryNowAndThen([sync = f->gl.sync, weak]() -> bool {
runEveryNowAndThen(
[&platform = mPlatform, context = mContext, handle = f->handle, weak]() -> bool {
auto result = weak.lock();
if (result) {
GLenum const status = glClientWaitSync(sync, 0, 0u);
auto const status = context.clientWaitSync(platform, handle);
result->status.store(status, std::memory_order_relaxed);
return (status != GL_TIMEOUT_EXPIRED);
return (status != OpenGLContext::FenceSync::Status::TIMEOUT_EXPIRED);
}
return true; // we're done
return true;
});
}
@@ -1332,9 +1353,6 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
assert_invariant(t->gl.target == GL_RENDERBUFFER);
glDeleteRenderbuffers(1, &t->gl.id);
}
if (t->gl.fence) {
glDeleteSync(t->gl.fence);
}
if (t->gl.sidecarRenderBufferMS) {
glDeleteRenderbuffers(1, &t->gl.sidecarRenderBufferMS);
}
@@ -1413,10 +1431,9 @@ void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
void OpenGLDriver::destroySync(Handle<HwSync> sh) {
DEBUG_MARKER()
if (sh) {
GLSync* s = handle_cast<GLSync*>(sh);
glDeleteSync(s->gl.sync);
mContext.destroyFenceSync(mPlatform, s->handle);
destruct(sh, s);
}
}
@@ -1640,7 +1657,7 @@ bool OpenGLDriver::isRenderTargetFormatSupported(TextureFormat format) {
// Three-component SRGB is a color-renderable texture format in core OpenGL on desktop.
case TextureFormat::SRGB8:
return BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL;
return mContext.isAtLeastGL(4, 5);
// Half-float formats, requires extension.
case TextureFormat::R16F:
@@ -1691,6 +1708,10 @@ bool OpenGLDriver::isAutoDepthResolveSupported() {
return true;
}
bool OpenGLDriver::isSRGBSwapChainSupported() {
return mPlatform.isSRGBSwapChainSupported();
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1966,7 +1987,9 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
auto& gl = mContext;
GLTexture* t = handle_cast<GLTexture *>(th);
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
assert_invariant(t->gl.target != GL_TEXTURE_2D_MULTISAMPLE);
#endif
// Note: glGenerateMimap can also fail if the internal format is not both
// color-renderable and filterable (i.e.: doesn't work for depth)
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
@@ -2007,8 +2030,13 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
gl.pixelStore(GL_UNPACK_ROW_LENGTH, GLint(p.stride));
gl.pixelStore(GL_UNPACK_ALIGNMENT, GLint(p.alignment));
gl.pixelStore(GL_UNPACK_SKIP_PIXELS, GLint(p.left));
gl.pixelStore(GL_UNPACK_SKIP_ROWS, GLint(p.top));
// This is equivalent to using GL_UNPACK_SKIP_PIXELS and GL_UNPACK_SKIP_ROWS
using PBD = PixelBufferDescriptor;
size_t const stride = p.stride ? p.stride : width;
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
void const* const buffer = static_cast<char const*>(p.buffer) + p.left * bpp + bpr * p.top;
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
@@ -2022,7 +2050,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), glFormat, glType, buffer);
break;
case SamplerType::SAMPLER_3D:
assert_invariant(zoffset + depth <= std::max(1u, t->depth >> level));
@@ -2031,7 +2059,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
@@ -2043,7 +2071,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
t->gl.target == GL_TEXTURE_CUBE_MAP_ARRAY);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, buffer);
break;
case SamplerType::SAMPLER_CUBEMAP: {
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
@@ -2059,7 +2087,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
GLenum const target = getCubemapTarget(zoffset + face);
glTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), glFormat, glType,
static_cast<uint8_t const*>(p.buffer) + faceSize * face);
static_cast<uint8_t const*>(buffer) + faceSize * face);
}
break;
}
@@ -2333,13 +2361,14 @@ SyncStatus OpenGLDriver::getSyncStatus(Handle<HwSync> sh) {
return SyncStatus::NOT_SIGNALED;
}
auto status = s->result->status.load(std::memory_order_relaxed);
using Status = OpenGLContext::FenceSync::Status;
switch (status) {
case GL_CONDITION_SATISFIED:
case GL_ALREADY_SIGNALED:
case Status::CONDITION_SATISFIED:
case Status::ALREADY_SIGNALED:
return SyncStatus::SIGNALED;
case GL_TIMEOUT_EXPIRED:
case Status::TIMEOUT_EXPIRED:
return SyncStatus::NOT_SIGNALED;
case GL_WAIT_FAILED:
case Status::FAILURE:
default:
return SyncStatus::ERROR;
}
@@ -2364,21 +2393,20 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
// glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3, for simplicity we just
// ignore it on GL (rather than having to do a runtime check).
if (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES &&
BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) {
if (!gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
if (attachmentCount) {
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
CHECK_GL_ERROR(utils::slog.e)
// glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3
#if defined(BACKEND_OPENGL_LEVEL_GLES30) || defined(BACKEND_OPENGL_VERSION_GL)
if ((mContext.isAtLeastGLES(3, 0) || mContext.isAtLeastGL(4, 3))
&& !gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
if (attachmentCount) {
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
} else {
// on GL desktop we assume we don't have glInvalidateFramebuffer, but even if the GPU is
// not a tiler, it's important to clear the framebuffer before drawing, as it resets
CHECK_GL_ERROR(utils::slog.e)
} else
#endif
{
// It's important to clear the framebuffer before drawing, as it resets
// the fb to a known state (resets fb compression and possibly other things).
// So we use glClear instead of glInvalidateFramebuffer
gl.disable(GL_SCISSOR_TEST);
@@ -2450,8 +2478,8 @@ void OpenGLDriver::endRenderPass(int) {
// glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3, for simplicity we just
// ignore it on GL (rather than having to do a runtime check).
if (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES &&
BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) {
#if defined(BACKEND_OPENGL_LEVEL_GLES30) || defined(BACKEND_OPENGL_VERSION_GL)
if (mContext.isAtLeastGLES(3, 0) || mContext.isAtLeastGL(4, 3)) {
auto effectiveDiscardFlags = discardFlags;
if (gl.bugs.invalidate_end_only_if_invalidate_start) {
effectiveDiscardFlags &= mRenderPassParams.flags.discardStart;
@@ -2467,6 +2495,7 @@ void OpenGLDriver::endRenderPass(int) {
CHECK_GL_ERROR(utils::slog.e)
}
}
#endif
#ifndef NDEBUG
// clear the discarded buffers in debug builds
@@ -2710,10 +2739,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
GLenum const glFormat = getFormat(p.format);
GLenum const glType = getType(p.type);
gl.pixelStore(GL_PACK_ROW_LENGTH, (GLint)p.stride);
gl.pixelStore(GL_PACK_ALIGNMENT, (GLint)p.alignment);
gl.pixelStore(GL_PACK_SKIP_PIXELS, (GLint)p.left);
gl.pixelStore(GL_PACK_SKIP_ROWS, (GLint)p.top);
gl.pixelStore(GL_PACK_ALIGNMENT, (GLint)p.alignment);
/*
* glReadPixel() operation...
@@ -2741,44 +2767,54 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
*/
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo);
// glReadPixel doesn't resolve automatically, but it does with the auto-resolve extension,
// which we're always emulating. So if we have a resolved fbo (fbo_read), use that instead.
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
using PBD = PixelBufferDescriptor;
// The PBO only needs to accommodate the area we're reading, with alignment.
auto const pboSize = (GLsizeiptr)PBD::computeDataSize(
p.format, p.type, width, height, p.alignment);
GLuint pbo;
glGenBuffers(1, &pbo);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, (GLsizeiptr)p.size, nullptr, GL_STATIC_DRAW);
glBufferData(GL_PIXEL_PACK_BUFFER, pboSize, nullptr, GL_STATIC_DRAW);
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, nullptr);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
CHECK_GL_ERROR(utils::slog.e)
// we're forced to make a copy on the heap because otherwise it deletes std::function<> copy
// constructor.
auto* pUserBuffer = new PixelBufferDescriptor(std::move(p));
whenGpuCommandsComplete([this, width, height, pbo, pUserBuffer]() mutable {
auto* const pUserBuffer = new PixelBufferDescriptor(std::move(p));
whenGpuCommandsComplete([this, width, height, pbo, pboSize, pUserBuffer]() mutable {
PixelBufferDescriptor& p = *pUserBuffer;
auto& gl = mContext;
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* vaddr = nullptr;
#if defined(__EMSCRIPTEN__)
std::unique_ptr<uint8_t> clientBuffer = std::make_unique<uint8_t>(p.size);
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, p.size, clientBuffer.get());
std::unique_ptr<uint8_t> clientBuffer = std::make_unique<uint8_t>(pboSize);
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, pboSize, clientBuffer.get());
vaddr = clientBuffer.get();
#else
vaddr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, (GLsizeiptr)p.size, GL_MAP_READ_BIT);
vaddr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pboSize, GL_MAP_READ_BIT);
#endif
if (vaddr) {
// now we need to flip the buffer vertically to match our API
size_t const stride = p.stride ? p.stride : width;
size_t const bpp = PixelBufferDescriptor::computeDataSize(
p.format, p.type, 1, 1, 1);
size_t const bpr = PixelBufferDescriptor::computeDataSize(
p.format, p.type, stride, 1, p.alignment);
char const* head = (char const*)vaddr + p.left * bpp + bpr * p.top;
char* tail = (char*)p.buffer + p.left * bpp + bpr * (p.top + height - 1);
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
size_t const dstBpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
char* pDst = (char*)p.buffer + p.left * bpp + dstBpr * (p.top + height - 1);
size_t const srcBpr = PBD::computeDataSize(p.format, p.type, width, 1, p.alignment);
char const* pSrc = (char const*)vaddr;
for (size_t i = 0; i < height; ++i) {
memcpy(tail, head, bpp * width);
head += bpr;
tail -= bpr;
memcpy(pDst, pSrc, bpp * width);
pSrc += srcBpr;
pDst -= dstBpr;
}
#if !defined(__EMSCRIPTEN__)
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
@@ -2845,7 +2881,7 @@ void OpenGLDriver::readBufferSubData(backend::BufferObjectHandle boh,
}
void OpenGLDriver::whenGpuCommandsComplete(std::function<void()> fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
OpenGLContext::FenceSync sync = mContext.createFenceSync(mPlatform);
mGpuCommandCompleteOps.emplace_back(sync, std::move(fn));
CHECK_GL_ERROR(utils::slog.e)
}
@@ -2858,15 +2894,16 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
auto& v = mGpuCommandCompleteOps;
auto it = v.begin();
while (it != v.end()) {
GLenum const status = glClientWaitSync(it->first, 0, 0);
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) {
using Status = OpenGLContext::FenceSync::Status;
auto const status = mContext.clientWaitSync(mPlatform, it->first);
if (status == Status::ALREADY_SIGNALED || status == Status::CONDITION_SATISFIED) {
it->second();
glDeleteSync(it->first);
mContext.destroyFenceSync(mPlatform, it->first);
it = v.erase(it);
} else if (UTILS_UNLIKELY(status == GL_WAIT_FAILED)) {
} else if (UTILS_UNLIKELY(status == Status::FAILURE)) {
// This should never happen, but is very problematic if it does, as we might leak
// some data depending on what the callback does. However, we clean up our own state.
glDeleteSync(it->first);
mContext.destroyFenceSync(mPlatform, it->first);
it = v.erase(it);
} else {
++it;
@@ -3203,7 +3240,7 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
useProgram(p);
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
#if defined(__ANDROID__)
// on Android, GLES3.1 and above entry-points are defined in glext
@@ -3212,7 +3249,7 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
#endif
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif
#endif // BACKEND_OPENGL_LEVEL_GLES31
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
+4 -6
View File
@@ -122,7 +122,6 @@ public:
GLenum target = 0;
GLenum internalFormat = 0;
GLuint sidecarRenderBufferMS = 0; // multi-sample sidecar renderbuffer
mutable GLsync fence = {};
// texture parameters go here too
GLfloat anisotropy = 1.0;
@@ -186,11 +185,10 @@ public:
struct GLSync : public HwSync {
using HwSync::HwSync;
struct State {
std::atomic<GLenum> status{ GL_TIMEOUT_EXPIRED };
std::atomic<OpenGLContext::FenceSync::Status> status{
OpenGLContext::FenceSync::Status::TIMEOUT_EXPIRED };
};
struct {
GLsync sync;
} gl;
OpenGLContext::FenceSync handle{};
std::shared_ptr<State> result{ std::make_shared<GLSync::State>() };
};
@@ -364,7 +362,7 @@ private:
// tasks executed on the main thread after the fence signaled
void whenGpuCommandsComplete(std::function<void()> fn) noexcept;
void executeGpuCommandsCompleteOps() noexcept;
std::vector<std::pair<GLsync, std::function<void()>>> mGpuCommandCompleteOps;
std::vector<std::pair<OpenGLContext::FenceSync, std::function<void()>>> mGpuCommandCompleteOps;
// tasks regularly executed on the main thread at until they return true
void runEveryNowAndThen(std::function<bool()> fn) noexcept;
@@ -27,6 +27,10 @@ Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
OpenGLPlatform::~OpenGLPlatform() noexcept = default;
bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
uint32_t OpenGLPlatform::createDefaultRenderTarget() noexcept {
return 0;
}
+18 -14
View File
@@ -121,11 +121,21 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
UTILS_NOUNROLL
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
const ShaderStage stage = static_cast<ShaderStage>(i);
GLenum glShaderType;
GLenum glShaderType{};
switch (stage) {
case ShaderStage::VERTEX: glShaderType = GL_VERTEX_SHADER; break;
case ShaderStage::FRAGMENT: glShaderType = GL_FRAGMENT_SHADER; break;
case ShaderStage::COMPUTE: glShaderType = GL_COMPUTE_SHADER; break;
case ShaderStage::VERTEX:
glShaderType = GL_VERTEX_SHADER;
break;
case ShaderStage::FRAGMENT:
glShaderType = GL_FRAGMENT_SHADER;
break;
case ShaderStage::COMPUTE:
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
glShaderType = GL_COMPUTE_SHADER;
#else
continue;
#endif
break;
}
if (UTILS_LIKELY(!shadersSource[i].empty())) {
@@ -182,13 +192,12 @@ std::string_view OpenGLProgram::process_GOOGLE_cpp_style_line_directive(OpenGLCo
return { source, len };
}
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
// macOS doesn't support GL_ARB_shading_language_packing
std::string_view OpenGLProgram::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
using namespace std::literals;
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
if (context.state.major == 4 && context.state.minor == 1 &&
!context.ext.ARB_shading_language_packing) {
#ifdef BACKEND_OPENGL_VERSION_GL
if (!context.isAtLeastGL(4, 2) && !context.ext.ARB_shading_language_packing) {
return R"(
// these don't handle denormals, NaNs or inf
@@ -226,7 +235,7 @@ highp uint packHalf2x16(vec2 v) {
}
)"sv;
}
}
#endif // BACKEND_OPENGL_VERSION_GL
return ""sv;
}
@@ -406,11 +415,6 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
const GLTexture* const t = sb->textureUnitEntries[j].texture;
GLuint const s = sb->textureUnitEntries[j].sampler;
if (t) { // program may not use all samplers of sampler group
if (UTILS_UNLIKELY(t->gl.fence)) {
glWaitSync(t->gl.fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(t->gl.fence);
t->gl.fence = nullptr;
}
gld->bindTexture(tmu, t);
gld->bindSampler(tmu, s);
}
@@ -34,8 +34,9 @@ OpenGLTimerQueryInterface::~OpenGLTimerQueryInterface() = default;
// ------------------------------------------------------------------------------------------------
TimerQueryNative::TimerQueryNative(OpenGLContext& context)
: gl(context) {
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
TimerQueryNative::TimerQueryNative(OpenGLContext&) {
}
TimerQueryNative::~TimerQueryNative() = default;
@@ -44,12 +45,12 @@ void TimerQueryNative::flush() {
}
void TimerQueryNative::beginTimeElapsedQuery(GLTimerQuery* query) {
gl.beginQuery(GL_TIME_ELAPSED, query->gl.query);
glBeginQuery(GL_TIME_ELAPSED, query->gl.query);
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNative::endTimeElapsedQuery(GLTimerQuery*) {
gl.endQuery(GL_TIME_ELAPSED);
glEndQuery(GL_TIME_ELAPSED);
CHECK_GL_ERROR(utils::slog.e)
}
@@ -62,14 +63,14 @@ bool TimerQueryNative::queryResultAvailable(GLTimerQuery* query) {
uint64_t TimerQueryNative::queryResult(GLTimerQuery* query) {
GLuint64 elapsedTime = 0;
// IOS doesn't have glGetQueryObjectui64v, we'll never end-up here on ios anyways
#ifndef IOS
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
glGetQueryObjectui64v(query->gl.query, GL_QUERY_RESULT, &elapsedTime);
#endif
CHECK_GL_ERROR(utils::slog.e)
return elapsedTime;
}
#endif
// ------------------------------------------------------------------------------------------------
OpenGLTimerQueryFence::OpenGLTimerQueryFence(OpenGLPlatform& platform)
@@ -85,7 +86,7 @@ OpenGLTimerQueryFence::OpenGLTimerQueryFence(OpenGLPlatform& platform)
});
exitRequested = mExitRequested;
if (!queue.empty()) {
Job job(queue.front());
Job const job(queue.front());
queue.erase(queue.begin());
lock.unlock();
job();
@@ -105,7 +106,7 @@ OpenGLTimerQueryFence::~OpenGLTimerQueryFence() {
}
void OpenGLTimerQueryFence::enqueue(OpenGLTimerQueryFence::Job&& job) {
std::unique_lock<utils::Mutex> lock(mLock);
std::unique_lock<utils::Mutex> const lock(mLock);
mQueue.push_back(std::forward<Job>(job));
mCondition.notify_one();
}
@@ -114,9 +115,9 @@ void OpenGLTimerQueryFence::flush() {
// Use calls to flush() as a proxy for when the GPU work started.
GLTimerQuery* query = mActiveQuery;
if (query) {
uint64_t elapsed = query->gl.emulation->elapsed.load(std::memory_order_relaxed);
uint64_t const elapsed = query->gl.emulation->elapsed.load(std::memory_order_relaxed);
if (!elapsed) {
uint64_t now = clock::now().time_since_epoch().count();
uint64_t const now = clock::now().time_since_epoch().count();
query->gl.emulation->elapsed.store(now, std::memory_order_relaxed);
//SYSTRACE_CONTEXT();
//SYSTRACE_ASYNC_BEGIN("gpu", query->gl.query);
@@ -139,7 +140,7 @@ void OpenGLTimerQueryFence::beginTimeElapsedQuery(GLTimerQuery* query) {
void OpenGLTimerQueryFence::endTimeElapsedQuery(GLTimerQuery* query) {
assert_invariant(mActiveQuery);
Platform::Fence* fence = mPlatform.createFence();
std::weak_ptr<GLTimerQuery::State> weak = query->gl.emulation;
std::weak_ptr<GLTimerQuery::State> const weak = query->gl.emulation;
mActiveQuery = nullptr;
//uint32_t cookie = cookie = query->gl.query;
push([&platform = mPlatform, fence, weak]() {
@@ -48,6 +48,8 @@ public:
virtual uint64_t queryResult(GLTimerQuery* query) = 0;
};
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
class TimerQueryNative : public OpenGLTimerQueryInterface {
public:
explicit TimerQueryNative(OpenGLContext& context);
@@ -58,9 +60,10 @@ private:
void endTimeElapsedQuery(GLTimerQuery* query) override;
bool queryResultAvailable(GLTimerQuery* query) override;
uint64_t queryResult(GLTimerQuery* query) override;
OpenGLContext& gl;
};
#endif
class OpenGLTimerQueryFence : public OpenGLTimerQueryInterface {
public:
explicit OpenGLTimerQueryFence(OpenGLPlatform& platform);
+25 -59
View File
@@ -16,16 +16,18 @@
#include "gl_headers.h"
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
#if defined(FILAMENT_IMPORT_ENTRY_POINTS)
#include <EGL/egl.h>
#include <mutex>
// for non EGL platforms, we'd need to implement this differently. Currently, it's not a problem.
template<typename T>
static void getProcAddress(T& pfn, const char* name) noexcept {
pfn = (T)eglGetProcAddress(name);
}
namespace glext {
#ifdef GL_QCOM_tiled_rendering
PFNGLSTARTTILINGQCOMPROC glStartTilingQCOM;
PFNGLENDTILINGQCOMPROC glEndTilingQCOM;
#endif
#ifdef GL_OES_EGL_image
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
#endif
@@ -46,7 +48,7 @@ PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR;
PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#endif
#ifdef GL_EXT_clip_control
PFNGLCLIPCONTROLEXTPROC glClipControl;
PFNGLCLIPCONTROLEXTPROC glClipControlEXT;
#endif
#if defined(__ANDROID__)
@@ -58,71 +60,35 @@ PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
static std::once_flag sGlExtInitialized;
void importGLESExtensionsEntryPoints() {
std::call_once(sGlExtInitialized, []() {
#ifdef GL_QCOM_tiled_rendering
glStartTilingQCOM =
(PFNGLSTARTTILINGQCOMPROC)eglGetProcAddress(
"glStartTilingQCOM");
glEndTilingQCOM =
(PFNGLENDTILINGQCOMPROC)eglGetProcAddress(
"glEndTilingQCOM");
#endif
std::call_once(sGlExtInitialized, +[]() {
#ifdef GL_OES_EGL_image
glEGLImageTargetTexture2DOES =
(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress(
"glEGLImageTargetTexture2DOES");
getProcAddress(glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES");
#endif
#if GL_EXT_debug_marker
glInsertEventMarkerEXT =
(PFNGLINSERTEVENTMARKEREXTPROC)eglGetProcAddress(
"glInsertEventMarkerEXT");
glPushGroupMarkerEXT =
(PFNGLPUSHGROUPMARKEREXTPROC)eglGetProcAddress(
"glPushGroupMarkerEXT");
glPopGroupMarkerEXT =
(PFNGLPOPGROUPMARKEREXTPROC)eglGetProcAddress(
"glPopGroupMarkerEXT");
getProcAddress(glInsertEventMarkerEXT, "glInsertEventMarkerEXT");
getProcAddress(glPushGroupMarkerEXT, "glPushGroupMarkerEXT");
getProcAddress(glPopGroupMarkerEXT, "glPopGroupMarkerEXT");
#endif
#if GL_EXT_multisampled_render_to_texture
glFramebufferTexture2DMultisampleEXT =
(PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress(
"glFramebufferTexture2DMultisampleEXT");
glRenderbufferStorageMultisampleEXT =
(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)eglGetProcAddress(
"glRenderbufferStorageMultisampleEXT");
getProcAddress(glFramebufferTexture2DMultisampleEXT, "glFramebufferTexture2DMultisampleEXT");
getProcAddress(glRenderbufferStorageMultisampleEXT, "glRenderbufferStorageMultisampleEXT");
#endif
#ifdef GL_KHR_debug
glDebugMessageCallbackKHR =
(PFNGLDEBUGMESSAGECALLBACKKHRPROC)eglGetProcAddress(
"glDebugMessageCallbackKHR");
glGetDebugMessageLogKHR =
(PFNGLGETDEBUGMESSAGELOGKHRPROC)eglGetProcAddress(
"glGetDebugMessageLogKHR");
getProcAddress(glDebugMessageCallbackKHR, "glDebugMessageCallbackKHR");
getProcAddress(glGetDebugMessageLogKHR, "glGetDebugMessageLogKHR");
#endif
#ifdef GL_EXT_disjoint_timer_query
glGetQueryObjectui64v =
(PFNGLGETQUERYOBJECTUI64VEXTPROC)eglGetProcAddress(
"glGetQueryObjectui64vEXT");
getProcAddress(glGetQueryObjectui64v, "glGetQueryObjectui64vEXT");
#endif
#ifdef GL_EXT_clip_control
getProcAddress(glClipControlEXT, "glClipControlEXT");
#endif
#if defined(__ANDROID__)
getProcAddress(glDispatchCompute, "glDispatchCompute");
#endif
});
#ifdef GL_EXT_clip_control
glClipControl =
(PFNGLCLIPCONTROLEXTPROC)eglGetProcAddress(
"glClipControlEXT");
#endif
#if defined(__ANDROID__)
glDispatchCompute =
(PFNGLDISPATCHCOMPUTEPROC)eglGetProcAddress(
"glDispatchCompute");
#endif
}
} // namespace glext
#endif
#endif // defined(FILAMENT_IMPORT_ENTRY_POINTS)
+125 -148
View File
@@ -17,6 +17,26 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GL_HEADERS_H
#define TNT_FILAMENT_BACKEND_OPENGL_GL_HEADERS_H
/*
* Configuration we aim to support:
*
* GL 4.5 headers
* - GL 4.1 runtime (for macOS)
* - GL 4.5 runtime
*
* GLES 2.0 headers
* - GLES 2.0 runtime Android only
*
* GLES 3.0 headers
* - GLES 3.0 runtime iOS and WebGL2 only
*
* GLES 3.1 headers
* - GLES 2.0 runtime
* - GLES 3.0 runtime
* - GLES 3.1 runtime
*/
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
#if defined(__EMSCRIPTEN__)
@@ -26,45 +46,6 @@
#endif
#include <GLES2/gl2ext.h>
/* The Android NDK doesn't expose extensions, fake it with eglGetProcAddress */
namespace glext {
// importGLESExtensionsEntryPoints is thread-safe and can be called multiple times.
// it is currently called from PlatformEGL.
void importGLESExtensionsEntryPoints();
#ifdef GL_QCOM_tiled_rendering
extern PFNGLSTARTTILINGQCOMPROC glStartTilingQCOM;
extern PFNGLENDTILINGQCOMPROC glEndTilingQCOM;
#endif
#ifdef GL_OES_EGL_image
extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
#endif
#ifdef GL_EXT_debug_marker
extern PFNGLINSERTEVENTMARKEREXTPROC glInsertEventMarkerEXT;
extern PFNGLPUSHGROUPMARKEREXTPROC glPushGroupMarkerEXT;
extern PFNGLPOPGROUPMARKEREXTPROC glPopGroupMarkerEXT;
#endif
#ifdef GL_EXT_multisampled_render_to_texture
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
extern PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT;
#endif
#ifdef GL_KHR_debug
extern PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
extern PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR;
#endif
#ifdef GL_EXT_disjoint_timer_query
extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#endif
#ifdef GL_EXT_clip_control
extern PFNGLCLIPCONTROLEXTPROC glClipControl;
#endif
#if defined(__ANDROID__)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
}
using namespace glext;
#elif defined(IOS)
#define GLES_SILENCE_DEPRECATION
@@ -85,111 +66,116 @@
#endif
#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1))
#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1"
#endif
/* Validate the header configurations we aim to support */
#if defined(GL_VERSION_4_5)
#elif defined(GL_ES_VERSION_3_1)
#elif defined(GL_ES_VERSION_3_0)
# if !defined(IOS) && !defined(__EMSCRIPTEN__)
# error "GLES 3.0 headers only supported on iOS and WebGL2"
# endif
#elif defined(GL_ES_VERSION_2_0)
# if !defined(__ANDROID__)
# error "GLES 2.0 headers only supported on Android"
# endif
#else
# error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.5"
#endif
/*
* Since we need ES3.1 headers and iOS only has ES3.0, we also define the constants we
* need to avoid many #ifdef in the actual code.
* GLES extensions
*/
#if defined(GL_ES_VERSION_2_0)
#if defined(GL_ES_VERSION_2_0) // this basically means all versions of GLES
#ifdef GL_EXT_disjoint_timer_query
# ifndef GL_TIME_ELAPSED
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
# endif
#if defined(IOS)
// iOS headers only provide prototypes, nothing to do.
#else
#define FILAMENT_IMPORT_ENTRY_POINTS
/* The Android NDK doesn't expose extensions, fake it with eglGetProcAddress */
namespace glext {
// importGLESExtensionsEntryPoints is thread-safe and can be called multiple times.
// it is currently called from PlatformEGL.
void importGLESExtensionsEntryPoints();
#ifdef GL_OES_EGL_image
extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
#endif
#ifdef GL_EXT_debug_marker
extern PFNGLINSERTEVENTMARKEREXTPROC glInsertEventMarkerEXT;
extern PFNGLPUSHGROUPMARKEREXTPROC glPushGroupMarkerEXT;
extern PFNGLPOPGROUPMARKEREXTPROC glPopGroupMarkerEXT;
#endif
#ifdef GL_EXT_multisampled_render_to_texture
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
extern PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT;
#endif
#ifdef GL_KHR_debug
extern PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
extern PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR;
#endif
#ifdef GL_EXT_clip_control
# ifndef GL_LOWER_LEFT
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
# endif
# ifndef GL_ZERO_TO_ONE
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
# endif
extern PFNGLCLIPCONTROLEXTPROC glClipControlEXT;
#endif
#ifdef GL_EXT_disjoint_timer_query
extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#endif
#if defined(__ANDROID__)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
} // namespace glext
using namespace glext;
#ifndef GL_TEXTURE_CUBE_MAP_ARRAY
# define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#endif
// Prevent lots of #ifdef's between desktop and mobile
#ifdef GL_EXT_disjoint_timer_query
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
#endif
#ifdef GL_EXT_clip_control
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
#endif
// we need GL_TEXTURE_CUBE_MAP_ARRAY defined, but we won't use it if the extension/feature
// is not available.
#if defined(GL_EXT_texture_cube_map_array)
# define GL_TEXTURE_CUBE_MAP_ARRAY GL_TEXTURE_CUBE_MAP_ARRAY_EXT
#else
# define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#endif
#if defined(GL_KHR_debug)
# ifndef GL_DEBUG_OUTPUT
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# endif
# ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_HIGH
# define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_MEDIUM
# define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_LOW
# define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_NOTIFICATION
# define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
# endif
# ifndef GL_DEBUG_TYPE_MARKER
# define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR
# endif
# ifndef GL_DEBUG_TYPE_ERROR
# define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR
# endif
# ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
# define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR
# endif
# ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
# define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR
# endif
# ifndef GL_DEBUG_TYPE_PORTABILITY
# define GL_DEBUG_TYPE_PORTABILITY GL_DEBUG_TYPE_PORTABILITY_KHR
# endif
# ifndef GL_DEBUG_TYPE_PERFORMANCE
# define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
# endif
# ifndef GL_DEBUG_TYPE_OTHER
# define GL_DEBUG_TYPE_OTHER GL_DEBUG_TYPE_OTHER_KHR
# endif
# define glDebugMessageCallback glDebugMessageCallbackKHR
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
# define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
# define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
# define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
# define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
# define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR
# define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR
# define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR
# define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR
# define GL_DEBUG_TYPE_PORTABILITY GL_DEBUG_TYPE_PORTABILITY_KHR
# define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
# define GL_DEBUG_TYPE_OTHER GL_DEBUG_TYPE_OTHER_KHR
# define glDebugMessageCallback glDebugMessageCallbackKHR
#endif
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
* requires ES3.1 headers */
#if !defined(GL_ES_VERSION_3_1)
#define GL_SHADER_STORAGE_BUFFER 0x90D2
#define GL_COMPUTE_SHADER 0x91B9
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
// FIXME: The GL_TIME_ELAPSED define is used unconditionally in Filament, but
// requires extension support.
#ifndef GL_TIME_ELAPSED
#define GL_TIME_ELAPSED 0x88BF
#endif
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
#endif
#endif // GL_ES_VERSION_2_0
// This is just to simplify the implementation (i.e. so we don't have to have #ifdefs everywhere)
#ifndef GL_OES_EGL_image_external
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
#endif
// This is an odd duck function that exists in WebGL 2.0 but not in OpenGL ES.
#if defined(__EMSCRIPTEN__)
extern "C" {
@@ -197,34 +183,25 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void *d
}
#endif
#define BACKEND_OPENGL_VERSION_GLES 0
#define BACKEND_OPENGL_VERSION_GL 1
#if defined(GL_ES_VERSION_2_0)
# define BACKEND_OPENGL_VERSION BACKEND_OPENGL_VERSION_GLES
#elif defined(GL_VERSION_4_1)
# define BACKEND_OPENGL_VERSION BACKEND_OPENGL_VERSION_GL
# define BACKEND_OPENGL_VERSION_GLES
#elif defined(GL_VERSION_4_5)
# define BACKEND_OPENGL_VERSION_GL
#else
# error "Unsupported header version"
#endif
#define BACKEND_OPENGL_LEVEL_GLES20 0
#define BACKEND_OPENGL_LEVEL_GLES30 1
#define BACKEND_OPENGL_LEVEL_GLES31 2
#if defined(GL_VERSION_4_1)
# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES30
#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_3_1)
# define BACKEND_OPENGL_LEVEL_GLES31
# ifdef __EMSCRIPTEN__
# error "__EMSCRIPTEN__ shouldn't be defined with GLES 3.1 headers"
# endif
#endif
#if defined(GL_ES_VERSION_3_1)
# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES31
#elif defined(GL_ES_VERSION_3_0)
# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES30
#elif defined(GL_ES_VERSION_2_0)
# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES20
#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_3_0)
# define BACKEND_OPENGL_LEVEL_GLES30
#endif
// This is just to simplify the implementation (i.e. so we don't have to have #ifdefs everywhere)
#ifndef GL_OES_EGL_image_external
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_2_0)
# define BACKEND_OPENGL_LEVEL_GLES20
#endif
#include "NullGLES.h"
@@ -171,9 +171,7 @@ void PlatformCocoaGL::terminate() noexcept {
bluegl::unbind();
}
Platform::SwapChain* PlatformCocoaGL::createSwapChain(void* nativewindow, uint64_t& flags) noexcept {
// Transparent SwapChain is not supported
flags &= ~SWAP_CHAIN_CONFIG_TRANSPARENT;
Platform::SwapChain* PlatformCocoaGL::createSwapChain(void* nativewindow, uint64_t flags) noexcept {
NSView* nsView = (__bridge NSView*)nativewindow;
CocoaGLSwapChain* swapChain = new CocoaGLSwapChain( nsView );
@@ -188,7 +186,7 @@ Platform::SwapChain* PlatformCocoaGL::createSwapChain(void* nativewindow, uint64
return swapChain;
}
Platform::SwapChain* PlatformCocoaGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
Platform::SwapChain* PlatformCocoaGL::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
NSView* nsView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width, height)];
// adding the pointer to the array retains the NSView
@@ -84,8 +84,8 @@ Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Pl
pImpl->mDefaultColorbuffer = renderbuffer[0];
pImpl->mDefaultDepthbuffer = renderbuffer[1];
CVReturn success = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nullptr,
pImpl->mGLContext, nullptr, &pImpl->mTextureCache);
UTILS_UNUSED_IN_RELEASE CVReturn success = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault,
nullptr, pImpl->mGLContext, nullptr, &pImpl->mTextureCache);
assert_invariant(success == kCVReturnSuccess);
pImpl->mExternalImageSharedGl = new CocoaTouchExternalImage::SharedGl();
@@ -99,13 +99,11 @@ void PlatformCocoaTouchGL::terminate() noexcept {
delete pImpl->mExternalImageSharedGl;
}
Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(void* nativewindow, uint64_t& flags) noexcept {
// Transparent swap chain is not supported
flags &= ~SWAP_CHAIN_CONFIG_TRANSPARENT;
Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(void* nativewindow, uint64_t flags) noexcept {
return (SwapChain*) nativewindow;
}
Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
CAEAGLLayer* glLayer = [CAEAGLLayer layer];
glLayer.frame = CGRectMake(0, 0, width, height);
@@ -94,14 +94,15 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
return nullptr;
}
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
// PlatofrmEGL is used with and without GLES, but this function is only
// meaningful when GLES is used.
#if defined(FILAMENT_IMPORT_ENTRY_POINTS)
importGLESExtensionsEntryPoints();
#endif
auto extensions = GLUtils::split(eglQueryString(mEGLDisplay, EGL_EXTENSIONS));
ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context");
ext.egl.KHR_gl_colorspace = extensions.has("EGL_KHR_gl_colorspace");
eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
@@ -115,7 +116,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
EGL_RED_SIZE, 8, // 2
EGL_GREEN_SIZE, 8, // 4
EGL_BLUE_SIZE, 8, // 6
EGL_ALPHA_SIZE, 0, // 8 : reserved to set ALPHA_SIZE below
EGL_ALPHA_SIZE, 8, // 8
EGL_DEPTH_SIZE, 24, // 10
EGL_RECORDABLE_ANDROID, 1, // 12
EGL_NONE // 14
@@ -142,66 +143,46 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
}
#endif
EGLConfig eglConfig = nullptr;
// config use for creating the context
EGLConfig eglConfig = EGL_NO_CONFIG_KHR;
// find an opaque config
// find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use
// for the dummy pbuffer surface.
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount)) {
logEglError("eglChooseConfig");
goto error;
}
if (configsCount == 0) {
// warn and retry without EGL_RECORDABLE_ANDROID
logEglError("eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it.");
configAttribs[12] = EGL_RECORDABLE_ANDROID;
configAttribs[13] = EGL_DONT_CARE;
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount) ||
configsCount == 0) {
logEglError("eglChooseConfig");
goto error;
}
}
// find a transparent config
configAttribs[8] = EGL_ALPHA_SIZE;
configAttribs[9] = 8;
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLTransparentConfig, 1, &configsCount) ||
(configAttribs[13] == EGL_DONT_CARE && configsCount == 0)) {
logEglError("eglChooseConfig");
goto error;
}
if (configsCount == 0) {
// warn and retry without EGL_RECORDABLE_ANDROID
// warn and retry without EGL_RECORDABLE_ANDROID
logEglError("eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it.");
// this is not fatal
configAttribs[12] = EGL_RECORDABLE_ANDROID;
configAttribs[13] = EGL_DONT_CARE;
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLTransparentConfig, 1, &configsCount) ||
configsCount == 0) {
logEglError("eglChooseConfig");
goto error;
}
// this is not fatal
configAttribs[12] = EGL_RECORDABLE_ANDROID;
configAttribs[13] = EGL_DONT_CARE;
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount) ||
configsCount == 0) {
logEglError("eglChooseConfig");
goto error;
}
}
if (!extensions.has("EGL_KHR_no_config_context")) {
// if we have the EGL_KHR_no_config_context, we don't need to worry about the config
// when creating the context, otherwise, we must always pick a transparent config.
eglConfig = mEGLConfig = mEGLTransparentConfig;
if (UTILS_UNLIKELY(!ext.egl.KHR_no_config_context)) {
// if we don't have the EGL_KHR_no_config_context the context must be created with
// the same config as the swapchain, so we have no choice but to create a
// transparent config.
eglConfig = mEGLConfig;
}
// the pbuffer dummy surface is always created with a transparent surface because
// either we have EGL_KHR_no_config_context and it doesn't matter, or we don't and
// we must use a transparent surface
mEGLDummySurface = eglCreatePbufferSurface(mEGLDisplay, mEGLTransparentConfig, pbufferAttribs);
if (mEGLDummySurface == EGL_NO_SURFACE) {
// create the dummy surface, just for being able to make the context current.
mEGLDummySurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, pbufferAttribs);
if (UTILS_UNLIKELY(mEGLDummySurface == EGL_NO_SURFACE)) {
logEglError("eglCreatePbufferSurface");
goto error;
}
mEGLContext = eglCreateContext(mEGLDisplay, eglConfig, (EGLContext)sharedContext, contextAttribs);
if (mEGLContext == EGL_NO_CONTEXT && sharedContext &&
extensions.has("EGL_KHR_create_context_no_error")) {
if (UTILS_UNLIKELY(mEGLContext == EGL_NO_CONTEXT && sharedContext &&
extensions.has("EGL_KHR_create_context_no_error"))) {
// context creation could fail because of EGL_CONTEXT_OPENGL_NO_ERROR_KHR
// not matching the sharedContext. Try with it.
contextAttribs[2] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR;
@@ -214,7 +195,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
goto error;
}
if (!makeCurrent(mEGLDummySurface, mEGLDummySurface)) {
if (UTILS_UNLIKELY(!makeCurrent(mEGLDummySurface, mEGLDummySurface))) {
// eglMakeCurrent failed
logEglError("eglMakeCurrent");
goto error;
@@ -263,36 +244,115 @@ void PlatformEGL::terminate() noexcept {
eglReleaseThread();
}
EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags) const {
EGLConfig config = EGL_NO_CONFIG_KHR;
EGLint configsCount;
EGLint configAttribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, (flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ? 8 : 0,
EGL_DEPTH_SIZE, 24,
EGL_RECORDABLE_ANDROID, 1,
EGL_NONE
};
if (UTILS_UNLIKELY(
!eglChooseConfig(mEGLDisplay, configAttribs, &config, 1, &configsCount))) {
logEglError("eglChooseConfig");
return EGL_NO_CONFIG_KHR;
}
if (UTILS_UNLIKELY(configsCount == 0)) {
// warn and retry without EGL_RECORDABLE_ANDROID
logEglError(
"eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it.");
configAttribs[12] = EGL_RECORDABLE_ANDROID;
configAttribs[13] = EGL_DONT_CARE;
if (UTILS_UNLIKELY(
!eglChooseConfig(mEGLDisplay, configAttribs, &config, 1, &configsCount) ||
configsCount == 0)) {
logEglError("eglChooseConfig");
return EGL_NO_CONFIG_KHR;
}
}
return config;
}
bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t& flags) noexcept {
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay,
(flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ?
mEGLTransparentConfig : mEGLConfig,
(EGLNativeWindowType)nativeWindow, nullptr);
void* nativeWindow, uint64_t flags) noexcept {
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
EGLint attribs[] = {
EGL_NONE, EGL_NONE,
EGL_NONE
};
if (ext.egl.KHR_gl_colorspace) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[0] = EGL_GL_COLORSPACE_KHR;
attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
}
}
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config,
(EGLNativeWindowType)nativeWindow, attribs);
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreateWindowSurface");
return nullptr;
}
if (!eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)) {
logEglError("eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)");
// this is not fatal
}
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
return (SwapChain*)sur;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
uint32_t width, uint32_t height, uint64_t& flags) noexcept {
uint32_t width, uint32_t height, uint64_t flags) noexcept {
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
EGLint attribs[] = {
EGL_WIDTH, EGLint(width),
EGL_HEIGHT, EGLint(height),
EGL_NONE, EGL_NONE,
EGL_NONE
};
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay,
(flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ?
mEGLTransparentConfig : mEGLConfig, attribs);
if (ext.egl.KHR_gl_colorspace) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[4] = EGL_GL_COLORSPACE_KHR;
attribs[5] = EGL_GL_COLORSPACE_SRGB_KHR;
}
}
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs);
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreatePbufferSurface");
@@ -366,7 +426,7 @@ FenceStatus PlatformEGL::waitFence(
OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new ExternalTexture{};
glGenTextures(1, &outTexture->id);
if (UTILS_LIKELY(ext.OES_EGL_image_external_essl3)) {
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
outTexture->target = GL_TEXTURE_EXTERNAL_OES;
} else {
// if texture external is not supported, revert to texture 2d
@@ -382,7 +442,7 @@ void PlatformEGL::destroyExternalImage(ExternalTexture* texture) noexcept {
bool PlatformEGL::setExternalImage(void* externalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
if (UTILS_LIKELY(ext.OES_EGL_image_external_essl3)) {
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
assert_invariant(texture->target == GL_TEXTURE_EXTERNAL_OES);
// the texture is guaranteed to be bound here.
#ifdef GL_OES_EGL_image
@@ -401,7 +461,7 @@ void PlatformEGL::initializeGlExtensions() noexcept {
const char * const extension = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i);
glExtensions.insert(extension);
}
ext.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
}
} // namespace filament::backend
@@ -239,16 +239,12 @@ void PlatformGLX::terminate() noexcept {
bluegl::unbind();
}
Platform::SwapChain* PlatformGLX::createSwapChain(void* nativeWindow, uint64_t& flags) noexcept {
// Transparent swap chain is not supported
flags &= ~SWAP_CHAIN_CONFIG_TRANSPARENT;
Platform::SwapChain* PlatformGLX::createSwapChain(void* nativeWindow, uint64_t flags) noexcept {
return (SwapChain*)nativeWindow;
}
Platform::SwapChain* PlatformGLX::createSwapChain(
uint32_t width, uint32_t height, uint64_t& flags) noexcept {
// Transparent swap chain is not supported
flags &= ~SWAP_CHAIN_CONFIG_TRANSPARENT;
uint32_t width, uint32_t height, uint64_t flags) noexcept {
int pbufferAttribs[] = {
GLX_PBUFFER_WIDTH, int(width),
GLX_PBUFFER_HEIGHT, int(height),
@@ -175,7 +175,7 @@ void PlatformWGL::terminate() noexcept {
bluegl::unbind();
}
Platform::SwapChain* PlatformWGL::createSwapChain(void* nativeWindow, uint64_t& flags) noexcept {
Platform::SwapChain* PlatformWGL::createSwapChain(void* nativeWindow, uint64_t flags) noexcept {
auto* swapChain = new WGLSwapChain();
swapChain->isHeadless = false;
@@ -194,7 +194,7 @@ Platform::SwapChain* PlatformWGL::createSwapChain(void* nativeWindow, uint64_t&
return (Platform::SwapChain*) swapChain;
}
Platform::SwapChain* PlatformWGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
Platform::SwapChain* PlatformWGL::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
auto* swapChain = new WGLSwapChain();
swapChain->isHeadless = true;
@@ -33,12 +33,12 @@ void PlatformWebGL::terminate() noexcept {
}
Platform::SwapChain* PlatformWebGL::createSwapChain(
void* nativeWindow, uint64_t& flags) noexcept {
void* nativeWindow, uint64_t flags) noexcept {
return (SwapChain*)nativeWindow;
}
Platform::SwapChain* PlatformWebGL::createSwapChain(
uint32_t width, uint32_t height, uint64_t& flags) noexcept {
uint32_t width, uint32_t height, uint64_t flags) noexcept {
// TODO: implement headless SwapChain
return nullptr;
}
@@ -29,23 +29,32 @@ using namespace bluevk;
namespace filament::backend {
Driver* PlatformVkAndroid::createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkAndroid::createDriver(void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
static const char* requiredInstanceExtensions[] = { "VK_KHR_android_surface" };
return VulkanDriverFactory::create(this, requiredInstanceExtensions, 1, driverConfig);
}
void* PlatformVkAndroid::createVkSurfaceKHR(void* nativeWindow, void* vkinstance, uint64_t flags) noexcept {
SurfaceBundle PlatformVkAndroid::createVkSurfaceKHR(void* nativeWindow, void* vkinstance,
uint64_t flags) noexcept {
const VkInstance instance = (VkInstance) vkinstance;
ANativeWindow* aNativeWindow = (ANativeWindow*) nativeWindow;
VkAndroidSurfaceCreateInfoKHR createInfo {
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
.window = aNativeWindow
};
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkResult result = vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC, &surface);
SurfaceBundle bundle {
.surface = VK_NULL_HANDLE,
.width = 0,
.height = 0
};
VkResult result = vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateAndroidSurfaceKHR error.");
return (void*) surface;
return bundle;
}
} // namespace filament::backend
@@ -29,7 +29,8 @@ public:
Driver* createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
@@ -27,7 +27,8 @@ namespace filament::backend {
class PlatformVkCocoa final : public VulkanPlatform {
public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
+13 -4
View File
@@ -34,6 +34,8 @@ using namespace bluevk;
namespace filament::backend {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkCocoa::createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
static const char* requiredInstanceExtensions[] = {
@@ -42,21 +44,28 @@ Driver* PlatformVkCocoa::createDriver(void* sharedContext, const Platform::Drive
return VulkanDriverFactory::create(this, requiredInstanceExtensions, 1, driverConfig);
}
void* PlatformVkCocoa::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
SurfaceBundle PlatformVkCocoa::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
// Obtain the CAMetalLayer-backed view.
NSView* nsview = (__bridge NSView*) nativeWindow;
ASSERT_POSTCONDITION(nsview, "Unable to obtain Metal-backed NSView.");
// Create the VkSurface.
ASSERT_POSTCONDITION(vkCreateMacOSSurfaceMVK, "Unable to load vkCreateMacOSSurfaceMVK.");
VkSurfaceKHR surface = nullptr;
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC, &surface);
SurfaceBundle bundle {
.surface = nullptr,
.width = 0,
.height = 0
};
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateMacOSSurfaceMVK error.");
return surface;
return bundle;
}
} // namespace filament::backend
@@ -27,7 +27,8 @@ namespace filament::backend {
class PlatformVkCocoaTouch final : public VulkanPlatform {
public:
Driver* createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
@@ -39,31 +39,37 @@ using namespace bluevk;
namespace filament::backend {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkCocoaTouch::createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
static const char* requestedExtensions[] = {"VK_MVK_ios_surface"};
return VulkanDriverFactory::create(this, requestedExtensions, 1, driverConfig);
}
void* PlatformVkCocoaTouch::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
SurfaceBundle PlatformVkCocoaTouch::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
SurfaceBundle bundle {
.surface = nullptr,
.width = 0,
.height = 0
};
#if METAL_AVAILABLE
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
// Create the VkSurface.
ASSERT_POSTCONDITION(vkCreateIOSSurfaceMVK, "Unable to load vkCreateIOSSurfaceMVK function.");
VkSurfaceKHR surface = nullptr;
VkIOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = metalLayer;
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC, &surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateIOSSurfaceMVK error.");
return surface;
#else
return nullptr;
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateIOSSurfaceMVK error.");
#endif
return bundle;
}
} // namespace filament::backend
@@ -28,6 +28,8 @@ using namespace bluevk;
namespace filament::backend {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkLinuxGGP::createDriver(
void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
@@ -46,8 +48,13 @@ Driver* PlatformVkLinuxGGP::createDriver(
#endif
}
void* PlatformVkLinuxGGP::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
SurfaceBundle PlatformVkLinuxGGP::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
SurfaceBundle bundle {
.surface = nullptr,
.width = 0,
.height = 0
};
#if defined(FILAMENT_SUPPORTS_GGP)
VkStreamDescriptorSurfaceCreateInfoGGP surface_create_info = {
VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP};
@@ -59,16 +66,14 @@ void* PlatformVkLinuxGGP::createVkSurfaceKHR(void* nativeWindow, void* instance,
ASSERT_PRECONDITION(fpCreateStreamDescriptorSurfaceGGP != nullptr,
"Error getting VkInstance "
"function vkCreateStreamDescriptorSurfaceGGP");
VkSurfaceKHR surface = nullptr;
VkResult res = fpCreateStreamDescriptorSurfaceGGP(
static_cast<VkInstance>(instance), &surface_create_info, nullptr,
&surface);
(VkSurfaceKHR*) &bundle.surface);
ASSERT_PRECONDITION(res == VK_SUCCESS, "Error in vulkan: %d", res);
return surface;
#else
PANIC_PRECONDITION("Filament does not support GGP.");
return nullptr;
#endif
return bundle;
}
} // namespace filament::backend
@@ -30,8 +30,8 @@ class PlatformVkLinuxGGP final : public VulkanPlatform {
void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
@@ -27,8 +27,19 @@
using namespace bluevk;
namespace {
typedef struct _wl {
struct wl_display *display;
struct wl_surface *surface;
uint32_t width;
uint32_t height;
} wl;
} // anonymous namespace
namespace filament::backend {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkLinuxWayland::createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
const char* requiredInstanceExtensions[] = {
@@ -38,15 +49,8 @@ Driver* PlatformVkLinuxWayland::createDriver(void* const sharedContext, const Pl
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]), driverConfig);
}
void* PlatformVkLinuxWayland::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
typedef struct _wl {
struct wl_display *display;
struct wl_surface *surface;
} wl;
VkSurfaceKHR surface = nullptr;
SurfaceBundle PlatformVkLinuxWayland::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
VkWaylandSurfaceCreateInfoKHR createInfo = {
@@ -56,11 +60,15 @@ void* PlatformVkLinuxWayland::createVkSurfaceKHR(void* nativeWindow, void* insta
.display = ptrval->display,
.surface = ptrval->surface
};
VkResult result = vkCreateWaylandSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
SurfaceBundle bundle {
.surface = VK_NULL_HANDLE,
.width = ptrval->width,
.height = ptrval->height
};
VkResult result = vkCreateWaylandSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWaylandSurfaceKHR error.");
return surface;
return bundle;
}
} // namespace filament::backend
@@ -30,7 +30,8 @@ public:
Driver* createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
@@ -29,6 +29,8 @@ using namespace bluevk;
namespace filament::backend {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
#ifdef FILAMENT_SUPPORTS_XCB
@@ -50,7 +52,8 @@ struct X11Functions {
void* library = nullptr;
} g_x11;
Driver* PlatformVkLinuxX11::createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformVkLinuxX11::createDriver(void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
const char* requiredInstanceExtensions[] = {
#ifdef FILAMENT_SUPPORTS_XCB
@@ -64,7 +67,8 @@ Driver* PlatformVkLinuxX11::createDriver(void* const sharedContext, const Platfo
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]), driverConfig);
}
void* PlatformVkLinuxX11::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
SurfaceBundle PlatformVkLinuxX11::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
if (g_x11.library == nullptr) {
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
ASSERT_PRECONDITION(g_x11.library, "Unable to open X11 library.");
@@ -85,7 +89,11 @@ void* PlatformVkLinuxX11::createVkSurfaceKHR(void* nativeWindow, void* instance,
}
VkSurfaceKHR surface = nullptr;
SurfaceBundle bundle {
.surface = nullptr,
.width = 0,
.height = 0
};
#ifdef FILAMENT_SUPPORTS_XCB
#ifdef FILAMENT_SUPPORTS_XLIB
@@ -101,8 +109,9 @@ const bool windowIsXCB = true;
.connection = mConnection,
.window = (xcb_window_t) ptrval,
};
vkCreateXcbSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
return surface;
vkCreateXcbSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
return bundle;
}
#endif
@@ -112,10 +121,11 @@ const bool windowIsXCB = true;
.dpy = mDisplay,
.window = (Window) nativeWindow,
};
vkCreateXlibSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
vkCreateXlibSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &bundle.surface);
#endif
return surface;
return bundle;
}
} // namespace filament::backend
@@ -37,7 +37,8 @@ public:
Driver* createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
@@ -27,15 +27,22 @@ using namespace bluevk;
namespace filament::backend {
Driver* PlatformVkWindows::createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
Driver* PlatformVkWindows::createDriver(void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
ASSERT_PRECONDITION(sharedContext == nullptr, "Vulkan does not support shared contexts.");
const char* requiredInstanceExtensions[] = { "VK_KHR_win32_surface" };
return VulkanDriverFactory::create(this, requiredInstanceExtensions, 1, driverConfig);
}
void* PlatformVkWindows::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
VkSurfaceKHR surface = nullptr;
SurfaceBundle PlatformVkWindows::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
SurfaceBundle bundle {
.surface = nullptr,
.width = 0,
.height = 0
};
HWND window = (HWND) nativeWindow;
VkWin32SurfaceCreateInfoKHR createInfo = {};
@@ -43,10 +50,11 @@ void* PlatformVkWindows::createVkSurfaceKHR(void* nativeWindow, void* instance,
createInfo.hwnd = window;
createInfo.hinstance = GetModuleHandle(nullptr);
VkResult result = vkCreateWin32SurfaceKHR((VkInstance) instance, &createInfo, nullptr, &surface);
VkResult result = vkCreateWin32SurfaceKHR((VkInstance) instance, &createInfo, nullptr,
(VkSurfaceKHR*) &bundle.surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWin32SurfaceKHR error.");
return surface;
return bundle;
}
} // namespace filament::backend
@@ -29,7 +29,8 @@ public:
Driver* createDriver(void* const sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
VulkanPlatform::SurfaceBundle createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
@@ -57,7 +57,7 @@
constexpr struct VkAllocationCallbacks* VKALLOC = nullptr;
constexpr static const int VK_REQUIRED_VERSION_MAJOR = 1;
constexpr static const int VK_REQUIRED_VERSION_MINOR = 0;
constexpr static const int VK_REQUIRED_VERSION_MINOR = 1;
// Maximum number of VkCommandBuffer handles managed simultaneously by VulkanCommands.
//
+54 -169
View File
@@ -75,6 +75,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform,
mContext.commands->setObserver(&mPipelineCache);
mPipelineCache.setDevice(mContext.device, mContext.allocator);
mPipelineCache.setDummyTexture(mContext.emptyTexture->getPrimaryImageView());
mReadPixels.initialize(mContext.device);
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -134,6 +136,9 @@ void VulkanDriver::terminate() {
void VulkanDriver::tick(int) {
mContext.commands->updateFences();
// Handle any posted tasks
runTaskHandler();
}
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
@@ -395,9 +400,14 @@ void VulkanDriver::createSyncR(Handle<HwSync> sh, int) {
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
const VkInstance instance = mContext.instance;
auto vksurface = (VkSurfaceKHR) mContextManager.createVkSurfaceKHR(nativeWindow, instance,
flags);
construct<VulkanSwapChain>(sch, mContext, mStagePool, vksurface);
auto bundle = mContextManager.createVkSurfaceKHR(nativeWindow, instance, flags);
VkSurfaceKHR surface = (VkSurfaceKHR) bundle.surface;
VkExtent2D fallback{bundle.width, bundle.height};
if (fallback.width > 0 && fallback.height > 0) {
construct<VulkanSwapChain>(sch, mContext, mStagePool, surface, fallback);
} else {
construct<VulkanSwapChain>(sch, mContext, mStagePool, surface);
}
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
@@ -635,6 +645,11 @@ bool VulkanDriver::isAutoDepthResolveSupported() {
return false;
}
bool VulkanDriver::isSRGBSwapChainSupported() {
// TODO: implement SWAP_CHAIN_CONFIG_SRGB_COLORSPACE
return false;
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
VkPhysicalDeviceProperties const& deviceProperties = mContext.physicalDeviceProperties;
switch (workaround) {
@@ -1001,31 +1016,35 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rt->transformClientRectToPlatform(&renderPassInfo.renderArea);
VkClearValue clearValues[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {};
VkClearValue clearValues[
MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT +
1] = {};
if (params.flags.clear != TargetBufferFlags::NONE) {
// NOTE: clearValues must be populated in the same order as the attachments array in
// VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is
// actually clearing that particular target.
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (fbkey.color[i]) {
VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.color.float32[0] = params.clearColor.r;
clearValue.color.float32[1] = params.clearColor.g;
clearValue.color.float32[2] = params.clearColor.b;
clearValue.color.float32[3] = params.clearColor.a;
// NOTE: clearValues must be populated in the same order as the attachments array in
// VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is
// actually clearing that particular target.
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (fbkey.color[i]) {
VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.color.float32[0] = params.clearColor.r;
clearValue.color.float32[1] = params.clearColor.g;
clearValue.color.float32[2] = params.clearColor.b;
clearValue.color.float32[3] = params.clearColor.a;
}
}
}
// Resolve attachments are not cleared but still have entries in the list, so skip over them.
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (rpkey.needsResolveMask & (1u << i)) {
renderPassInfo.clearValueCount++;
// Resolve attachments are not cleared but still have entries in the list, so skip over them.
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (rpkey.needsResolveMask & (1u << i)) {
renderPassInfo.clearValueCount++;
}
}
if (fbkey.depth) {
VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.depthStencil = {(float) params.clearDepth, 0};
}
renderPassInfo.pClearValues = &clearValues[0];
}
if (fbkey.depth) {
VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++];
clearValue.depthStencil = {(float) params.clearDepth, 0};
}
renderPassInfo.pClearValues = &clearValues[0];
vkCmdBeginRenderPass(cmdbuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
@@ -1311,152 +1330,18 @@ void VulkanDriver::stopCapture(int) {
}
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) {
const VkDevice device = mContext.device;
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& pbd) {
VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget*>(src);
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
assert_invariant(srcTexture);
const VkFormat srcFormat = srcTexture->getVkFormat();
const bool swizzle = srcFormat == VK_FORMAT_B8G8R8A8_UNORM;
// Create a host visible, linearly tiled image as a staging area.
VkImageCreateInfo imageInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = srcFormat,
.extent = { width, height, 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_LINEAR,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImage stagingImage;
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
VkMemoryRequirements memReqs;
VkDeviceMemory stagingMemory;
vkGetImageMemoryRequirements(device, stagingImage, &memReqs);
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
};
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
// TODO: don't flush/wait here, this should be asynchronous
mContext.commands->flush();
mContext.commands->wait();
// Transition the staging image layout.
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
transitionImageLayout(cmdbuffer, {
.image = stagingImage,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.subresources = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
.srcAccessMask = 0,
.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
});
const VulkanAttachment srcAttachment = srcTarget->getColor(0);
VkImageCopy imageCopyRegion = {
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = srcAttachment.level,
.baseArrayLayer = srcAttachment.layer,
.layerCount = 1,
},
.srcOffset = {
.x = (int32_t) x,
.y = (int32_t) (srcTarget->getExtent().height - (height + y)),
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1,
},
.extent = {
.width = width,
.height = height,
.depth = 1,
},
};
// Transition the source image layout (which might be the swap chain)
const VkImageSubresourceRange srcRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = srcAttachment.level,
.levelCount = 1,
.baseArrayLayer = srcAttachment.layer,
.layerCount = 1,
};
srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// Perform the copy into the staging area. At this point we know that the src layout is
// TRANSFER_SRC_OPTIMAL and the staging area is GENERAL.
UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D();
assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width);
assert_invariant(imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingImage, VK_IMAGE_LAYOUT_GENERAL,
1, &imageCopyRegion);
// Restore the source image layout. Between driver API calls, color images are always kept in
// UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout).
srcTexture->transitionLayout(cmdbuffer, srcRange,
getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
// TODO: don't flush/wait here -- we should do this asynchronously
// Flush and wait.
mContext.commands->flush();
mContext.commands->wait();
VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
VkSubresourceLayout subResourceLayout;
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
// Map image memory so we can start copying from it.
const uint8_t* srcPixels;
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
srcPixels += subResourceLayout.offset;
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), getComponentCount(srcFormat),
srcPixels, subResourceLayout.rowPitch, width, height, swizzle)) {
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
}
vkUnmapMemory(device, stagingMemory);
vkDestroyImage(device, stagingImage, nullptr);
vkFreeMemory(device, stagingMemory, nullptr);
scheduleDestroy(std::move(pbd));
mReadPixels.run(
srcTarget, x, y, width, height, mContext.graphicsQueueFamilyIndex, std::move(pbd),
getTaskHandler(),
[&context = mContext](uint32_t reqs, VkFlags flags) {
return context.selectMemoryType(reqs, flags);
},
[this](PixelBufferDescriptor&& pbd) {
this->scheduleDestroy(std::move(pbd));
});
}
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,

Some files were not shown because too many files have changed in this diff Show More