Compare commits

...

86 Commits

Author SHA1 Message Date
prideout
a82d93914e Bump to v1.4.2 2019-10-30 13:49:36 -07:00
Philip Rideout
389349198f gltfio-jni: Fix double-init panic caused by build issues.
Filament keeps a global static list of engines for verification purposes
which was being recreated multiple times, causing a panic. After a lot
of experimentation with the build, I was not able to fix this while
keeping the current scheme of dynamically loading both libraries.

As a "solution", we now staticly link filament into gltfio-jni, and ask
clients to load only that.

Fixes #1811.
2019-10-30 09:51:14 -07:00
Philip Rideout
225a844e0f Minor improvements to Engine validation.
This enhances the assert added in 2548da9 in the following ways:

- Adds console output at startup to help detect "double load" bugs.
- Adds PRETTY_FUNCTION to show where the invalid usage is coming from.
- Tweaks the wording since use-after-destroy is not always the cause.
- Consolidates usage of static globals into a helper class.

Motivated by #1811.
2019-10-29 15:48:29 -07:00
Mathias Agopian
f018c719a6 a test for the skinning math 2019-10-29 15:38:40 -07:00
Philip Rideout
2a229571ad Minor fixes to gltf-bloom demo. 2019-10-29 10:20:28 -07:00
Romain Guy
35959b547c Use unnormalized vectors to compute bump mapping. (#1813)
If we assume the tangents and bitangents were generated using mikktspace,
we should use the unnormalized vectors, as indicated at mikktspace.com
or in http://www.metalliandy.com/mikktspace/downloads/mikktspace/mikktspace.h
(see last comment at the end of the file).
2019-10-29 09:41:15 +00:00
Philip Rideout
796007377d Revert the Engine validation commit.
This needs to wait until code review and was pushed by accident.

This reverts commit bb90103e74.
2019-10-28 16:14:07 -07:00
Philip Rideout
5c73d0ae75 Update package.json for npm release. 2019-10-28 16:05:45 -07:00
Philip Rideout
bb90103e74 Minor improvements to Engine validation.
This enhances the assert added in 2548da9 in the following ways:

- Adds console output at startup to help detect "double load" bugs.
- Adds PRETTY_FUNCTION to show where the invalid usage is coming from.
- Tweak the wording since use-after-destroy is not always the cause.

Motivated by #1811.
2019-10-28 14:08:54 -07:00
Ben Doherty
0149bc504d Fix Windows GitHub Action (#1814) 2019-10-28 12:22:42 -07:00
Philip Rideout
3db035520b gltf-bloom now invokes the gltfio animator.
Tested with CesiumMan.
2019-10-28 09:32:39 -07:00
Philip Rideout
618b59932c gltfio: Add Java / Kotlin bindings for Animator.
Fixes #1529.
2019-10-28 09:32:39 -07:00
Mathias Agopian
9ef05ad456 better fix for NoopDriver on iOS 2019-10-24 14:17:29 -07:00
Adrian Perez
c8a0dbb59a Fix OpenGL ES 3.0 support on iOS (#1803) 2019-10-24 12:43:23 -07:00
dsternfeld7
35a0b045f8 Adds missing include in NoopDriver that prevented the correct shader model from being picked when using the Noop backend. 2019-10-24 10:58:16 -07:00
Ben Doherty
a4369c833d Add README section on Filament CMake options (#1801) 2019-10-23 13:27:08 -07:00
Philip Rideout
354f1da708 Enhance KHR_debug output + code cleanup. 2019-10-22 12:41:34 -07:00
Philip Rideout
7e47d257d1 OpenGL Driver: add support for KHR_debug.
This extension dumps out some interesting vendor-specific information
to the log, which could be useful for debugging. Interestingly, some
of Android demos produce this output on Pixel 3:

  KHR_debug: EsxBufferObject::Map - Ignoring EsxBufferMapUnsyncedBit

Enabled for debug builds only.
2019-10-22 12:41:34 -07:00
Ben Doherty
c3d2b3578d Add Javadoc for Colors, Box, and MathUtils (#1796) 2019-10-21 17:59:05 -07:00
tpsiaki
783aa06a46 Fix typo in Noop on mobile. (#1799) 2019-10-21 17:41:32 -07:00
Mathias Agopian
faa82ff35a implement diag() in TMatSquareFunctions
this requires using 'auto' as the return type.
2019-10-21 14:27:40 -07:00
Ben Doherty
3008123d6b Update RELEASE_NOTES for v1.4.1 (#1793) 2019-10-18 13:36:34 -07:00
Ben Doherty
2d2987066d Implement user-controlled frame presentation in Metal (#1760) 2019-10-17 17:11:34 -07:00
Mathias Agopian
c84ec04efa remove unneeded loop in TransformManager
during a transaction, it makes no sense iterating through all
children just so that we can iterate through their children...
and do nothing else.
2019-10-17 16:17:32 -07:00
Mathias Agopian
197b45c1d1 fix a double-free when exiting the FroxelData test
We were destroying the engine manually (by calling delete) which is
not allowed.

Also made FEngine::shutdown() private.
2019-10-17 16:17:12 -07:00
Mathias Agopian
af9ecf0a61 minor cleanup based on clion suggestions 2019-10-17 16:16:51 -07:00
Ben Doherty
530fb58337 Add Javadoc to IndexBuffer and VertexBuffer (#1788) 2019-10-16 17:59:53 -07:00
Philip Rideout
6a891021b1 MSVC fixes for Vulkan.
Should fix #1772, hopefully.
2019-10-16 17:37:33 -07:00
Ben Doherty
0e7c68b50d Fix, assertion using bones with Metal backend (#1787) 2019-10-16 15:54:08 -07:00
Ben Doherty
32a32d3d21 Remove iOS OpenGL ES 3.1 hack (#1781) 2019-10-16 10:51:55 -07:00
Ben Doherty
fca71813d4 Use CMAKE_COMMAND variable in place of cmake (#1783) 2019-10-16 10:50:55 -07:00
Philip Rideout
3618f14531 Add Android sample for Camera Stream.
This simple Kotlin app projects a live feed on the side of a cube.
It looks very similar to the `lit-cube` demo but there is a new Kotlin
class called `CameraHelper` that creates Camera2 objects, starts
the capture session, creates the Filament texture, etc.

For now, this app uses SurfaceTexture. This is a starting point and
provides a testing ground for tentative improvements to our Stream API.
2019-10-16 09:05:24 -07:00
Romain Guy
9de3e76a53 Remove the use of reflection where it's not necessary (#1782)
Fixes #1510
2019-10-15 17:54:02 -07:00
Romain Guy
e472fdbce4 Remove Kokoro Windows presubmit 2019-10-15 17:40:24 -07:00
Mathias Agopian
c9fcedb40b some optimizations to the math code (#1779)
- we use by-value parameters in some places, which helps the compiler
  (not sure why, but assuming references can be aliased and the
   compiler is cautious about that).
  This helps matrix multiplies (which is almost never inlined) and
  transpose (but that's almost always inlined and merged with
  something else, so the gain here is not completely real)

- remove support for matrices-of-matrices because this wasn't complete
  anyways.

- improve matrix * scalar by inlining the loop manually instead of
  going through *=, this saves some extra writes.
2019-10-15 17:22:54 -07:00
Mathias Agopian
bd8c3e9181 don't limit the near plane to ~1mm (#1778)
We were not checking Camera's preconditions properly -- we assumed that
near < 1/1024 meant "near == 0".

Now we check preconditions properly and revert to the same hardcoded
default if they're not met -- doesn't matter what the default is,
it's there only to avoid NaNs and Infs elsewhere -- rendering will
be bogus eitherway.

Fixes #1766
2019-10-15 17:22:38 -07:00
Gregory Popovitch
26d0aa76f8 README.md: add build steps for Visual Studio 2019 / MSVC (#1775) 2019-10-15 12:02:08 -07:00
Ben Doherty
20f4ec6576 Use platform-specific temporary directories for CircularBuffer (#1769) 2019-10-15 12:01:03 -07:00
Ben Doherty
d0bc23473c Fix crash when using Filamat lite library (#1780) 2019-10-15 10:49:59 -07:00
Ben Doherty
c33f6adf62 Remove BUILD_DEMO CMake option (#1776) 2019-10-14 09:09:46 -07:00
Ben Doherty
d4efa105b7 Set up Windows build through GitHub actions (#1767) 2019-10-11 09:28:57 -07:00
Ben Doherty
e8641ce257 Additional fixes for MSVC (#1765) 2019-10-10 16:45:06 -07:00
Ben Doherty
d7a4bdc3f1 Combine matdbg dependencies into single shared library (#1764) 2019-10-10 12:45:51 -07:00
Philip Rideout
e66215295c gltfio: use the new sparse support in cgltf. 2019-10-09 18:45:27 -07:00
Philip Rideout
552bb3e6db Update cgltf with sparse functionality. 2019-10-09 18:45:27 -07:00
Mathias Agopian
a96da05ab8 fix CircularBuffer ashmem fallback (#1763) 2019-10-09 16:48:22 -07:00
Ben Doherty
fb5ee4a9d0 Add Javadoc comments to Material and MaterialInstance (#1696) 2019-10-08 15:56:26 -07:00
Philip Rideout
da13067b57 libmath: prevent NaN's in slerp due to acos.
Fixes #1749.
2019-10-08 15:37:48 -07:00
Ben Doherty
d48d69c765 Use custom Git checkout action (#1756) 2019-10-08 15:22:09 -07:00
Mathias Agopian
5098b2af77 SwapChain javadoc 2019-10-08 10:31:32 -07:00
Mathias Agopian
5576d6b014 TextureSampler javadoc 2019-10-08 10:31:14 -07:00
Mathias Agopian
b072399229 TransformManager javadoc 2019-10-08 10:30:54 -07:00
Mathias Agopian
4f0ab43d58 Stream Javadoc 2019-10-08 10:27:53 -07:00
Philip Rideout
3f742880b5 gltfio: do not abort for sparse data, warn instead.
Sparse accessors are easy to punt gracefully, we can emit a warning
and instead use the dense "base" data.

Also, after cgltf support for this lands, we will need to apply sparse
data in ResourceLoader, so this adds a bit of plumbing to prep for that.

Relates to #1727.
2019-10-08 08:18:46 -07:00
Mathias Agopian
0704194460 javadoc for Renderer
also made resetUserTime and getUserTime public
2019-10-07 16:13:56 -07:00
Mathias Agopian
56e747f558 Scene javadoc 2019-10-07 16:13:38 -07:00
Ben Doherty
61c22f2295 Fix culling in MaterialInstance (#1750) 2019-10-07 16:08:50 -07:00
Mathias Agopian
c4bbaeda86 improve CircularBuffer error message 2019-10-04 13:18:09 -07:00
Philip Rideout
678ffc7503 gltfio: do not loop animation samplers, fixes #1730. 2019-10-04 08:48:35 -07:00
Philip Rideout
a080a47268 Add RenderTarget javadoc. 2019-10-04 08:47:55 -07:00
Philip Rideout
82e8104539 Fix javadoc warnings. 2019-10-04 08:47:55 -07:00
Mathias Agopian
11bd8e72ef harden lispsm code against div-by-zero 2019-10-03 22:58:26 -07:00
Mathias Agopian
03c2a90206 noexcept-ize libmath a bit move
this gets rid of a few ide-warnings about static initialization of
arrays with math types.
2019-10-03 17:12:35 -07:00
Mathias Agopian
c38ab64b6a Update Engine.cpp 2019-10-03 17:12:18 -07:00
Mathias Agopian
2548da9aed validate Engine in all Builder::build() methods
We now assert if the Engine given is invalid,
which would happen if it was used after being
destroyed.

Builder::build() is a reasonable place for this since it's generally
not in the critical path.
2019-10-03 17:12:18 -07:00
Ben Doherty
2fa08123a0 Add an iOS runner for backend-test (#1720) 2019-10-03 17:02:28 -07:00
Mathias Agopian
20c3d45b9c Engine.flushAndWait() is not needed in most places 2019-10-03 16:04:22 -07:00
Philip Rideout
c8af3226de OpenGL driver: fix error with uvec4 attribs, clean up Vulkan/Metal. (#1735)
The vertex shader in the material variant for skinning-and-morphing
declares a `uvec4` attribute that can be unused and disabled, but still
needs to be typed correctly.

The fix is motivated by WebGL, but the issue might occur with other
OpenGL implementations that have strict drivers.

Fixes #1726
2019-10-03 15:42:07 -07:00
Romain Guy
4b669fb1c7 Fix formatting 2019-10-03 14:25:39 -07:00
Mathias Agopian
340f6d721a Texture javadoc 2019-10-03 13:25:06 -07:00
Philip Rideout
489f3ff3c9 Add javadoc for View and Viewport. (#1734)
This CL also fixes warnings in IndirectLight and adds a new C++ getter
to View for consistency with Java.
2019-10-03 08:38:10 -07:00
Mathias Agopian
566d3613d1 Skybox javadoc 2019-10-02 15:58:57 -07:00
Mathias Agopian
e691cfa9e3 javadoc fixes for IndirectLight 2019-10-02 15:57:31 -07:00
Romain Guy
a4a1bf07aa Use our own constants instead of M_PI, M_*, etc. (#1733)
These constants are not part of the standard. We instead use our own
constexpr definitions in the filament::math namespace, as part of
the scalar.h include.
2019-10-02 15:31:38 -07:00
Ben Doherty
7ea7d1d5f2 Fix, matinfo doesn't work on Windows (#1732) 2019-10-02 13:20:24 -07:00
Philip Rideout
686c68c627 View API: minor doc fixes, add getRenderTarget. (#1723)
Since getRenderTarget() already exists in Java, we should also provide
it for C++ clients.
2019-10-02 10:13:42 -07:00
Romain Guy
c652dcfd2a Fix warning 2019-10-02 08:56:39 -07:00
Ben Doherty
01fab5a6e4 Update gtest to latest master (#1718) 2019-10-01 16:48:10 -07:00
Philip Rideout
16a5adcfde Fix quat slerp so it takes shortest path.
Fixes #1716.

This regressed in c65e561c59.
2019-10-01 16:22:00 -07:00
Philip Rideout
d550d36637 Add javadoc for RenderableManager, fix javadoc warnings. 2019-10-01 16:09:05 -07:00
Mathias Agopian
3857c1a9b2 javadoc for IndirectLight 2019-10-01 13:32:54 -07:00
Philip Rideout
2f8bc9239b Add JS binding to unary Camera::setExposure. 2019-10-01 12:39:40 -07:00
Philip Rideout
9de934e2bd Add Java annotations to setMorphWeights.
Noticed this while working on javadoc stuff.
2019-10-01 11:16:15 -07:00
Philip Rideout
769f1db011 Fix ambiguous atomic-to-bool conversion. 2019-10-01 10:43:15 -07:00
Philip Rideout
da72c58146 mac_runner: disable warning. 2019-10-01 10:43:15 -07:00
Ben Doherty
ef34dd2461 Fix operator overrides for Clang / MSVC on Windows (#1715) 2019-09-30 18:39:38 -07:00
553 changed files with 78617 additions and 24069 deletions

View File

@@ -12,7 +12,15 @@ jobs:
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v1
- name: Checkout Filament
run: |
git version
git init $GITHUB_WORKSPACE
cd $GITHUB_WORKSPACE
git remote add origin https://github.com/google/filament
git config gc.auto 0
git fetch --tags --prune --progress --no-recurse-submodules origin +${GITHUB_REF}:${GITHUB_REF/refs\//refs\/remote\/}
git checkout --progress --force ${GITHUB_REF/refs\//refs\/remote\/}
- name: Run build script
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
@@ -20,12 +28,39 @@ jobs:
env:
TARGET: presubmit
build-windows:
name: build-windows
runs-on: windows-latest
steps:
- name: Checkout Filament
run: |
git version
git init %GITHUB_WORKSPACE%
cd %GITHUB_WORKSPACE%
git remote add origin https://github.com/google/filament
git config gc.auto 0
git fetch --tags --prune --progress --no-recurse-submodules origin +%GITHUB_REF%:%GITHUB_REF:refs/=refs/remote/%
git checkout --progress --force %GITHUB_REF:refs/=refs/remote/%
shell: cmd
- name: Run build script
run: |
build\windows\build-github.bat
build-android:
name: build-android
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Checkout Filament
run: |
git version
git init $GITHUB_WORKSPACE
cd $GITHUB_WORKSPACE
git remote add origin https://github.com/google/filament
git config gc.auto 0
git fetch --tags --prune --progress --no-recurse-submodules origin +${GITHUB_REF}:${GITHUB_REF/refs\//refs\/remote\/}
git checkout --progress --force ${GITHUB_REF/refs\//refs\/remote\/}
- name: Run build script
run: |
cd build/android && ./build.sh ${TARGET}
@@ -37,7 +72,15 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Checkout Filament
run: |
git version
git init $GITHUB_WORKSPACE
cd $GITHUB_WORKSPACE
git remote add origin https://github.com/google/filament
git config gc.auto 0
git fetch --tags --prune --progress --no-recurse-submodules origin +${GITHUB_REF}:${GITHUB_REF/refs\//refs\/remote\/}
git checkout --progress --force ${GITHUB_REF/refs\//refs\/remote\/}
- name: Run build script
run: |
cd build/ios && ./build.sh ${TARGET}
@@ -49,7 +92,15 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Checkout Filament
run: |
git version
git init $GITHUB_WORKSPACE
cd $GITHUB_WORKSPACE
git remote add origin https://github.com/google/filament
git config gc.auto 0
git fetch --tags --prune --progress --no-recurse-submodules origin +${GITHUB_REF}:${GITHUB_REF/refs\//refs\/remote\/}
git checkout --progress --force ${GITHUB_REF/refs\//refs\/remote\/}
- name: Run build script
run: |
cd build/web && ./build.sh ${TARGET}

View File

@@ -355,6 +355,28 @@ function(list_licenses OUTPUT MODULES)
configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT})
endfunction(list_licenses)
set(COMBINE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/build/linux/combine-static-libs.sh")
if (WIN32)
set(COMBINE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/build/windows/combine-static-libs.bat")
set(CMAKE_AR "lib.exe")
endif()
# Add a custom command to TARGET that combines the static libraries in DEPS into a single archive.
function(combine_static_libs TARGET OUTPUT DEPS)
# Loop through the dependent libraries and query their location on disk.
set(DEPS_FILES )
foreach(DEPENDENCY ${DEPS})
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
endforeach()
add_custom_command(
TARGET ${TARGET} POST_BUILD
COMMAND "${COMBINE_SCRIPT}" "${CMAKE_AR}" "${OUTPUT}" ${DEPS_FILES}
COMMENT "Combining ${target} dependencies into single shared library"
VERBATIM
)
endfunction()
# ==================================================================================================
# Configuration for CMAKE_CROSSCOMPILING.
# ==================================================================================================

View File

@@ -249,6 +249,28 @@ $ ./build.sh -j release
If you use CMake directly instead of the build script, pass `-DENABLE_JAVA=OFF` to CMake instead.
### Filament-specific CMake Options
The following CMake options are boolean options specific to Filament:
- `ENABLE_JAVA`: Compile Java projects: requires a JDK and the JAVA_HOME env var
- `ENABLE_LTO`: Enable link-time optimizations if supported by the compiler
- `FILAMENT_BUILD_FILAMAT`: Build filamat and JNI buildings
- `FILAMENT_SUPPORTS_METAL`: Include the Metal backend
- `FILAMENT_SUPPORTS_VULKAN`: Include the Vulkan backend
- `GENERATE_JS_DOCS`: Build WebGL documentation and tutorials
- `INSTALL_BACKEND_TEST`: Install the backend test library so it can be consumed on iOS
- `USE_EXTERNAL_GLES3`: Experimental: Compile Filament against OpenGL ES 3
To turn an option on or off:
```
$ cd <cmake-build-directory>
$ cmake . -DOPTION=ON # Relace OPTION with the option name, set to ON / OFF
```
Options can also be set with the CMake GUI.
### Linux
Make sure you've installed the following dependencies:
@@ -341,6 +363,33 @@ See [ios/samples/README.md](./ios/samples/README.md) for more information.
### Windows
#### Building on Windows with the Visual Studio 2019 compiler
Install the following components:
- [Visual Studio 2019](https://www.visualstudio.com/downloads)
- [Python 3.7](https://www.python.org/ftp/python/3.7.0/python-3.7.0.exe)
- [CMake 3.14 or later](https://github.com/Kitware/CMake/releases/download/v3.14.7/cmake-3.14.7-win64-x64.msi)
Open the `x64 Native Tools Command Prompt for VS 2019`.
Create a working directory, and run cmake in it:
```
> mkdir out
> cd out
> cmake ..
```
Then, you should be able to load the generated solution file `TNT.sln` in Visual Studio and build the `material_sandbox` project.
Run it from the `out` directory with:
```
> samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj
```
#### Building on Windows with the Clang compiler
The following instructions have been tested on a machine running Windows 10. They should take you
from a machine with only the operating system to a machine able to build and run Filament.

View File

@@ -3,6 +3,32 @@
This file contains one line summaries of commits that are worthy of mentioning in release notes.
A new header is inserted each time a *tag* is created.
## v1.4.2
- Cleaned up the validation strategy in Engine (checks for use-after-destroy etc).
- OpenGL: Fixed ES 3.0 support on iOS.
- OpenGL: Added support for KHR_debug in debug builds.
- gltfio: Added Java / Kotlin bindings for Animator.
- gltfio: Fixed panic with the Android gltf-bloom demo.
- gltfio: Java clients should no longer call Filament#init.
## v1.4.1
- Added missing API documentation.
- Fixed crash for sandboxed macOS apps using Filament.
- Fixed an issue that limited the camera near plane to ~1mm.
- Added Android sample for Camera Stream.
- Fixed an Xcode assertion when rendering skinned meshes using the Metal backend.
- Added support for Core Animation / Metal frame synchronization with Metal backend.
- Fixed an issue with culling in `MaterialInstance`.
- Fix additional compatibility issues with MSVC, including the Vulkan backend.
- matdbg: fixed missing symbol issue when linking against debug builds.
- filamat: fixed crash when using the "lite" version of the library.
- matinfo: Fix a crash with on Windows.
- gltfio: fixed an animation loop bug.
- gltfio: added support for sparse accessors.
- Add JS binding to unary `Camera::setExposure`.
## v1.4.0
- API Breakage: Simplified public-facing Fence API.

View File

@@ -50,57 +50,66 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer -ff
set(CMAKE_SHARED_LINKER_FLAGS" ${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
set(CMAKE_SHARED_LINKER_FLAGS" ${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic-functions")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_SOURCE_DIR}/libfilament-jni.map")
add_library(filament-jni SHARED
src/main/cpp/Camera.cpp
src/main/cpp/Colors.cpp
src/main/cpp/VertexBuffer.cpp
src/main/cpp/Engine.cpp
src/main/cpp/EntityManager.cpp
src/main/cpp/Fence.cpp
src/main/cpp/IndexBuffer.cpp
src/main/cpp/IndirectLight.cpp
src/main/cpp/LightManager.cpp
src/main/cpp/Material.cpp
src/main/cpp/MaterialInstance.cpp
src/main/cpp/MathUtils.cpp
src/main/cpp/RenderableManager.cpp
src/main/cpp/Renderer.cpp
src/main/cpp/RenderTarget.cpp
src/main/cpp/Scene.cpp
src/main/cpp/SkyBox.cpp
src/main/cpp/Stream.cpp
src/main/cpp/Texture.cpp
src/main/cpp/TextureSampler.cpp
src/main/cpp/TransformManager.cpp
src/main/cpp/View.cpp
# Android specific
src/main/cpp/nativewindow/Android.cpp
# Private utils
src/main/cpp/Filament.cpp
# Common utils
../common/CallbackUtils.cpp
../common/NioUtils.cpp
add_library(filament-jni-obj OBJECT
src/main/cpp/Camera.cpp
src/main/cpp/Colors.cpp
src/main/cpp/VertexBuffer.cpp
src/main/cpp/Engine.cpp
src/main/cpp/EntityManager.cpp
src/main/cpp/Fence.cpp
src/main/cpp/IndexBuffer.cpp
src/main/cpp/IndirectLight.cpp
src/main/cpp/LightManager.cpp
src/main/cpp/Material.cpp
src/main/cpp/MaterialInstance.cpp
src/main/cpp/MathUtils.cpp
src/main/cpp/RenderableManager.cpp
src/main/cpp/Renderer.cpp
src/main/cpp/RenderTarget.cpp
src/main/cpp/Scene.cpp
src/main/cpp/SkyBox.cpp
src/main/cpp/Stream.cpp
src/main/cpp/Texture.cpp
src/main/cpp/TextureSampler.cpp
src/main/cpp/TransformManager.cpp
src/main/cpp/View.cpp
# Android specific
src/main/cpp/nativewindow/Android.cpp
)
target_link_libraries(filament-jni
filament
backend
filaflat
filabridge
geometry
ibl
utils
log
GLESv3
EGL
android
jnigraphics
)
if (NOT DISABLE_FILAMENT_JNI)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-jni.map")
add_library(filament-jni SHARED
$<TARGET_OBJECTS:filament-jni-obj>
# Private utils
src/main/cpp/Filament.cpp
# Common utils
../common/CallbackUtils.cpp
../common/NioUtils.cpp
)
target_link_libraries(filament-jni
filament
backend
filaflat
filabridge
geometry
ibl
utils
log
GLESv3
EGL
android
jnigraphics
)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(filament-jni bluevk smol-v)
endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(filament-jni bluevk smol-v)
endif()

View File

@@ -252,6 +252,13 @@ Java_com_google_android_filament_Engine_nDestroyEntity(JNIEnv*, jclass,
engine->destroy(entity);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv *env, jclass clazz,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
engine->flushAndWait();
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL

View File

@@ -47,6 +47,13 @@ Java_com_google_android_filament_Skybox_nBuilderShowSun(JNIEnv *env, jclass type
builder->showSun(show);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Skybox_nBuilderIntensity(JNIEnv *env, jclass clazz,
jlong nativeSkyBoxBuilder, jfloat intensity) {
Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder;
builder->intensity(intensity);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Skybox_nBuilderBuild(JNIEnv *env, jclass type,
jlong nativeSkyBoxBuilder, jlong nativeEngine) {
@@ -68,3 +75,10 @@ Java_com_google_android_filament_Skybox_nGetLayerMask(JNIEnv *env, jclass type,
Skybox *skybox = (Skybox *) nativeSkybox;
return static_cast<jint>(skybox->getLayerMask());
}
extern "C" JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_Skybox_nGetIntensity(JNIEnv *env, jclass clazz,
jlong nativeSkybox) {
Skybox *skybox = (Skybox *) nativeSkybox;
return static_cast<jint>(skybox->getIntensity());
}

View File

@@ -19,12 +19,25 @@ package com.google.android.filament;
import android.support.annotation.NonNull;
import android.support.annotation.Size;
/**
* An axis-aligned 3D box represented by its center and half-extent.
*
* The half-extent is a vector representing the distance from the center to the edge of the box in
* each dimension. For example, a box of size 2 units in X, 4 units in Y, and 10 units in Z would
* have a half-extent of (1, 2, 5).
*/
public class Box {
private final float[] mCenter = new float[3];
private final float[] mHalfExtent = new float[3];
/**
* Default-initializes the 3D box to have a center and half-extent of (0,0,0).
*/
public Box() { }
/**
* Initializes the 3D box from its center and half-extent.
*/
public Box(float centerX, float centerY, float centerZ,
float halfExtentX, float halfExtentY, float halfExtentZ) {
mCenter[0] = centerX;
@@ -35,6 +48,13 @@ public class Box {
mHalfExtent[2] = halfExtentZ;
}
/**
* Initializes the 3D box from its center and half-extent.
*
* @param center a float array with XYZ coordinates representing the center of the box
* @param halfExtent a float array with XYZ coordinates representing half the size of the box in
* each dimension
*/
public Box(@NonNull @Size(min = 3) float[] center, @NonNull @Size(min = 3) float[] halfExtent) {
mCenter[0] = center[0];
mCenter[1] = center[1];
@@ -44,21 +64,37 @@ public class Box {
mHalfExtent[2] = halfExtent[2];
}
/**
* Sets the center of of the 3D box.
*/
public void setCenter(float centerX, float centerY, float centerZ) {
mCenter[0] = centerX;
mCenter[1] = centerY;
mCenter[2] = centerZ;
}
/**
* Sets the half-extent of the 3D box.
*/
public void setHalfExtent(float halfExtentX, float halfExtentY, float halfExtentZ) {
mHalfExtent[0] = halfExtentX;
mHalfExtent[1] = halfExtentY;
mHalfExtent[2] = halfExtentZ;
}
/**
* Returns the center of the 3D box.
*
* @return an XYZ float array of size 3
*/
@NonNull @Size(min = 3)
public float[] getCenter() { return mCenter; }
/**
* Returns the half-extent from the center of the 3D box.
*
* @return an XYZ float array of size 3
*/
@NonNull @Size(min = 3)
public float[] getHalfExtent() { return mHalfExtent; }
}

View File

@@ -459,7 +459,6 @@ public class Camera {
* between 50 and 25600.
*
* @see LightManager
* @see Exposure
* @see #setExposure(float)
*/
public void setExposure(float aperture, float shutterSpeed, float sensitivity) {
@@ -477,8 +476,7 @@ public class Camera {
* the exposure manually. This can be typically achieved by setting the exposure to
* 1.0.
*
* @see Light
* @see Exposure
* @see LightManager
* @see #setExposure(float, float, float)
*/
public void setExposure(float exposure) {

View File

@@ -28,6 +28,9 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* Utilities to manipulate and convert colors.
*/
public class Colors {
private Colors() {
}
@@ -37,23 +40,67 @@ public class Colors {
public @interface LinearColor {
}
/**
* Types of RGB colors.
*/
public enum RgbType {
/** The color is defined in sRGB space. */
SRGB,
/** The color is defined in linear space. */
LINEAR
}
/**
* Types of RGBA colors.
*/
public enum RgbaType {
/**
* The color is defined in sRGB space and the RGB values have not been premultiplied by the
* alpha (for instance, a 50% transparent red is <1,0,0,0.5>).
*/
SRGB,
/**
* The color is defined in linear space and the RGB values have not been premultiplied by
* the alpha (for instance, a 50% transparent red is <1,0,0,0.5>).
*/
LINEAR,
/**
* The color is defined in sRGB space and the RGB values have been premultiplied by the
* alpha (for instance, a 50% transparent red is <0.5,0,0,0.5>).
*/
PREMULTIPLIED_SRGB,
/**
* The color is defined in linear space and the RGB values have been premultiplied by the
* alpha (for instance, a 50% transparent red is <0.5,0,0,0.5>).
*/
PREMULTIPLIED_LINEAR
}
/**
* Type of color conversion to use when converting to/from sRGB and linear spaces.
*/
public enum Conversion {
/** Accurate conversion using the sRGB standard. */
ACCURATE,
/** Fast conversion using a simple gamma 2.2 curve. */
FAST
}
/**
* Converts an RGB color to linear space, the conversion depends on the specified type.
*
* @param type the color space of the RGB color values provided
* @param r the red component
* @param g the green component
* @param b the blue component
*
* @return an RGB float array of size 3 with the result of the conversion
*/
@NonNull
@Size(3)
@LinearColor
@@ -61,6 +108,14 @@ public class Colors {
return toLinear(type, new float[] { r, g, b });
}
/**
* Converts an RGB color to linear space, the conversion depends on the specified type.
*
* @param type the color space of the RGB color values provided
* @param rgb an RGB float array of size 3, will be modified
*
* @return the passed-in <code>rgb</code> array, after applying the conversion
*/
@NonNull
@Size(min = 3)
@LinearColor
@@ -68,6 +123,17 @@ public class Colors {
return (type == RgbType.LINEAR) ? rgb : toLinear(Conversion.ACCURATE, rgb);
}
/**
* Converts an RGBA color to linear space, with pre-multiplied alpha.
*
* @param type the color space and type of RGBA color values provided
* @param r the red component
* @param g the green component
* @param b the blue component
* @param a the alpha component
*
* @return an RGBA float array of size 4 with the result of the conversion
*/
@NonNull
@Size(4)
@LinearColor
@@ -75,6 +141,14 @@ public class Colors {
return toLinear(type, new float[] { r, g, b, a });
}
/**
* Converts an RGBA color to linear space, with pre-multiplied alpha.
*
* @param type the color space of the RGBA color values provided
* @param rgba an RGBA float array of size 4, will be modified
*
* @return the passed-in <code>rgba</code> array, after applying the conversion
*/
@NonNull
@Size(min = 4)
@LinearColor
@@ -97,6 +171,15 @@ public class Colors {
return rgba;
}
/**
* Converts an RGB color in sRGB space to an RGB color in linear space.
*
* @param conversion the conversion algorithm to use
* @param rgb an RGB float array of at least size 3, will be modified
*
* @return the passed-in <code>rgb</code> array, after applying the conversion. The alpha
* channel, if present, is left unmodified.
*/
@NonNull
@LinearColor
public static float[] toLinear(@NonNull Conversion conversion, @NonNull @Size(min = 3) float[] rgb) {
@@ -116,6 +199,14 @@ public class Colors {
return rgb;
}
/**
* Converts a correlated color temperature to a linear RGB color in sRGB space. The temperature
* must be expressed in Kelvin and must be in the range 1,000K to 15,000K.
*
* @param temperature the temperature, in Kelvin
*
* @return an RGB float array of size 3 with the result of the conversion
*/
@NonNull
@Size(3)
@LinearColor
@@ -125,6 +216,14 @@ public class Colors {
return color;
}
/**
* Converts a CIE standard illuminant series D to a linear RGB color in sRGB space. The
* temperature must be expressed in Kelvin and must be in the range 4,000K to 25,000K.
*
* @param temperature the temperature, in Kelvin
*
* @return an RGB float array of size 3 with the result of the conversion
*/
@NonNull
@Size(3)
@LinearColor

View File

@@ -582,9 +582,14 @@ public class Engine {
* Kicks the hardware thread (e.g.: the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that this doesn't guarantee that the
* hardware is actually finished.
*
* <p>This is typically used right after destroying the <code>SwapChain</code>,
* in cases where a guarantee about the SwapChain destruction is needed in a timely fashion,
* such as when responding to Android's
* {@link android.view.SurfaceHolder.Callback#surfaceDestroyed surfaceDestroyed}.</p>
*/
public void flushAndWait() {
Fence.waitAndDestroy(createFence(), Fence.Mode.FLUSH);
nFlushAndWait(getNativeObject());
}
@UsedByReflection("TextureHelper.java")
@@ -626,6 +631,7 @@ public class Engine {
private static native void nDestroyTexture(long nativeEngine, long nativeTexture);
private static native void nDestroyRenderTarget(long nativeEngine, long nativeTarget);
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);

View File

@@ -23,6 +23,17 @@ import android.support.annotation.Nullable;
import java.nio.Buffer;
import java.nio.BufferOverflowException;
/**
* A buffer containing vertex indices into a <code>VertexBuffer</code>. Indices can be 16 or 32 bit.
* The buffer itself is a GPU resource, therefore mutating the data can be relatively slow.
* Typically these buffers are constant.
*
* It is possible, and even encouraged, to use a single index buffer for several
* <code>Renderables</code>.
*
* @see VertexBuffer
* @see RenderableManager
*/
public class IndexBuffer {
private long mNativeObject;
@@ -36,8 +47,14 @@ public class IndexBuffer {
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
/**
* Type of the index buffer.
*/
public enum IndexType {
/** 16-bit indices */
USHORT,
/** 32-bit indices */
UINT,
}
@@ -46,18 +63,45 @@ public class IndexBuffer {
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Size of the index buffer in elements.
*
* @param indexCount number of indices the <code>IndexBuffer</code> can hold
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder indexCount(@IntRange(from = 1) int indexCount) {
nBuilderIndexCount(mNativeBuilder, indexCount);
return this;
}
/**
* Type of the index buffer, 16-bit or 32-bit.
*
* @param indexType type of indices stored in the <code>IndexBuffer</code>
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder bufferType(@NonNull IndexType indexType) {
nBuilderBufferType(mNativeBuilder, indexType.ordinal());
return this;
}
/**
* Creates and returns the <code>IndexBuffer</code> object. After creation, the index buffer
* is uninitialized. Use {@link #setBuffer} to initialized the <code>IndexBuffer</code>.
*
* @param engine reference to the {@link Engine} to associate this <code>IndexBuffer</code>
* with
*
* @return the newly created <code>IndexBuffer</code> object
*
* @exception IllegalStateException if the IndexBuffer could not be created
*
* @see #setBuffer
*/
@NonNull
public IndexBuffer build(@NonNull Engine engine) {
long nativeIndexBuffer = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -85,24 +129,69 @@ public class IndexBuffer {
}
}
/**
* Returns the size of this <code>IndexBuffer</code> in elements.
*
* @return the number of indices the <code>IndexBuffer</code> holds
*/
@IntRange(from = 0)
public int getIndexCount() {
return nGetIndexCount(getNativeObject());
}
/**
* Asynchronously copy-initializes this <code>IndexBuffer</code> from the data provided.
*
* @param engine reference to the {@link Engine} to associate this
* <code>IndexBuffer</code> with
* @param buffer a CPU-side {@link Buffer} with the data used to initialize the
* <code>IndexBuffer</code>. <code>buffer</code> should contain raw,
* untyped data that will be interpreted as either 16-bit or 32-bits
* indices based on the <code>IndexType</code> of this
* <code>IndexBuffer</code>.
*/
public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer) {
setBuffer(engine, buffer, 0, 0, null, null);
}
/**
* Asynchronously copy-initializes a region of this <code>IndexBuffer</code> from the data
* provided.
*
* @param engine reference to the {@link Engine} to associate this
* <code>IndexBuffer</code> with
* @param buffer a CPU-side {@link Buffer} with the data used to initialize the
* <code>IndexBuffer</code>. <code>buffer</code> should contain raw,
* untyped data that will be interpreted as either 16-bit or 32-bits
* indices based on the <code>IndexType</code> of this
* <code>IndexBuffer</code>.
* @param destOffsetInBytes offset in <i>bytes</i> into the <code>IndexBuffer</code>
* @param count number of buffer elements to consume, defaults to
* <code>buffer.remaining()</code>
*/
public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer,
@IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count) {
setBuffer(engine, buffer, destOffsetInBytes, count, null, null);
}
/**
* Valid handler types:
* - Android: Handler, Executor
* - Other: Executor
* Asynchronously copy-initializes a region of this <code>IndexBuffer</code> from the data
* provided.
*
* @param engine reference to the {@link Engine} to associate this
* <code>IndexBuffer</code> with
* @param buffer a CPU-side {@link Buffer} with the data used to initialize the
* <code>IndexBuffer</code>. <code>buffer</code> should contain raw,
* untyped data that will be interpreted as either 16-bit or 32-bits
* indices based on the <code>IndexType</code> of this
* <code>IndexBuffer</code>.
* @param destOffsetInBytes offset in <i>bytes</i> into the <code>IndexBuffer</code>
* @param count number of buffer elements to consume, defaults to
* <code>buffer.remaining()</code>
* @param handler an {@link java.util.concurrent.Executor Executor}. On Android this
* can also be a {@link android.os.Handler Handler}.
* @param callback a callback executed by <code>handler</code> when <code>buffer</code>
* is no longer needed.
*/
public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer,
@IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count,

View File

@@ -23,6 +23,63 @@ import android.support.annotation.Size;
import com.google.android.filament.proguard.UsedByReflection;
/**
* <code>IndirectLight</code> is used to simulate environment lighting, a form of global illumination.
*
* <p>Environment lighting has a two components:</p>
* <ol>
* <li>irradiance</li>
* <li>reflections (specular component)</li>
* </ol>
*
* <p>Environments are usually captured as high-resolution HDR equirectangular images and processed
* by the <b>cmgen</b> tool to generate the data needed by {@link IndirectLight}.</p>
*
* <p>Currently {@link IndirectLight} is intended to be used for "distant probes", that is, to represent
* global illumination from a distant (i.e. at infinity) environment, such as the sky or distant
* mountains. Only a single {@link IndirectLight} can be used in a {@link Scene}.
* This limitation will be lifted in the future.</p>
*
*
* <h1>Creation and destruction</h1>
*
* <p>An {@link IndirectLight} object is created using the {@link IndirectLight.Builder} and
* destroyed by calling {@link Engine#destroyIndirectLight}.</p>
*
* <pre>
* Engine engine = Engine.create();
*
* Scene scene = engine.createScene();
*
* IndirectLight environment = new IndirectLight.Builder()
* .reflections(cubemap)
* .irradiance(numBands, sphericalHarmonicsCoefficients)
* .build(engine);
*
* scene.setIndirectLight(environment);
* </pre>
*
*
* <h1>Irradiance</h1>
*
* <p>The irradiance represents the light that comes from the environment and shines an
* object's surface. It is represented as
* <a href="https://en.wikipedia.org/wiki/Spherical_harmonics">Spherical Harmonics</a> (SH) of 1, 2 or
* 3 bands, respectively 1, 4 or 9 coefficients.</p>
*
* <p>Use the <b>cmgen</b> tool to generate the Spherical Harmonics for a given environment.</p>
*
*
* <h1>Reflections</h1>
*
* <p>The reflections on object surfaces (specular component) is calculated from a specially
* filtered cubemap pyramid generated by the <b>cmgen</b> tool.</p>
*
* @see Scene
* @see LightManager
* @see Texture
* @see Skybox
*/
public class IndirectLight {
long mNativeObject;
@@ -31,22 +88,97 @@ public class IndirectLight {
mNativeObject = indirectLight;
}
/**
* Use <code>Builder</code> to construct an <code>IndirectLight</code> object instance.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
/**
* Use <code>Builder</code> to construct an <code>IndirectLight</code> object instance.
*/
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Set the reflections cubemap mipmap chain.
*
* @param cubemap A mip-mapped cubemap generated by <b>cmgen</b>. Each cubemap level
* encodes the irradiance for a roughness level.
*
* @return This Builder, for chaining calls.
*
*/
@NonNull
public Builder reflections(@NonNull Texture cubemap) {
nBuilderReflections(mNativeBuilder, cubemap.getNativeObject());
return this;
}
/**
* Sets the irradiance as Spherical Harmonics.
*
* <p>The irradiance coefficients must be pre-convolved by <code>&lt n &sdot l &gt</code> and
* pre-multiplied by the Lambertian diffuse BRDF <code>1/&pi</code> and
* specified as Spherical Harmonics coefficients.</p>
*
* <p>Additionally, these Spherical Harmonics coefficients must be pre-scaled by the
* reconstruction factors A<sup>l,m</sup>.</p>
*
* <p>The final coefficients can be generated using the <code>cmgen</code> tool.</p>
*
* <p>The index in the <code>sh</code> array is given by:
* <br><code>index(l, m) = 3 &times (l * (l + 1) + m)</code>
* <br><code>sh[index(l,m) + 0] = L<sub>R</sub><sup>l,m</sup>
* &times 1/&pi
* &times A<sup>l,m</sup>
* &times C<sup>l</sup> </code>
* <br><code>sh[index(l,m) + 1] = L<sub>G</sub><sup>l,m</sup>
* &times 1/&pi
* &times A<sup>l,m</sup>
* &times C<sup>l</sup> </code>
* <br><code>sh[index(l,m) + 2] = L<sub>B</sub><sup>l,m</sup>
* &times 1/&pi
* &times A<sup>l,m</sup>
* &times C<sup>l</sup> </code>
* </p>
*
* <center>
* <table border="1" cellpadding="3">
* <tr><th> index </th><th> l </th><th> m </th><th> A<sup>l,m</sup> </th><th> C<sup>l</sup> </th>
* <th> 1/&pi &times A<sup>l,m</sup> &times C<sup>l</sup></th></tr>
* <tr align="right"><td>0</td><td>0</td><td> 0</td><td> 0.282095</td><td>3.1415926</td><td> 0.282095</td></tr>
* <tr align="right"><td>1</td><td>1</td><td>-1</td><td>-0.488602</td><td>2.0943951</td><td>-0.325735</td></tr>
* <tr align="right"><td>2</td><td>1</td><td> 0</td><td> 0.488602</td><td>2.0943951</td><td> 0.325735</td></tr>
* <tr align="right"><td>3</td><td>1</td><td> 1</td><td>-0.488602</td><td>2.0943951</td><td>-0.325735</td></tr>
* <tr align="right"><td>4</td><td>2</td><td>-2</td><td> 1.092548</td><td>0.785398 </td><td> 0.273137</td></tr>
* <tr align="right"><td>5</td><td>2</td><td>-1</td><td>-1.092548</td><td>0.785398 </td><td>-0.273137</td></tr>
* <tr align="right"><td>6</td><td>2</td><td> 0</td><td> 0.315392</td><td>0.785398 </td><td> 0.078848</td></tr>
* <tr align="right"><td>7</td><td>2</td><td> 1</td><td>-1.092548</td><td>0.785398 </td><td>-0.273137</td></tr>
* <tr align="right"><td>8</td><td>2</td><td> 2</td><td> 0.546274</td><td>0.785398 </td><td> 0.136569</td></tr>
* </table>
* </center>
*
*
* <p>Only 1, 2 or 3 bands are allowed.</p>
*
* <p>Because the coefficients are pre-scaled, <code>sh[0]</code> is the environment's
* average irradiance.</p>
*
* @param bands Number of spherical harmonics bands. Must be 1, 2 or 3.
* @param sh Array containing the spherical harmonics coefficients.
* The size of the array must be <code>3 &times bands<sup>2</sup></code>
* (i.e. 1, 4 or 9 <code>float3</code> coefficients respectively).
*
* @return This Builder, for chaining calls.
*
* @exception ArrayIndexOutOfBoundsException if the <code>sh</code> array length is smaller
* than 3 &times bands<sup>2</sup>
*/
@NonNull
public Builder irradiance(@IntRange(from=1, to=3) int bands, @NonNull float[] sh) {
switch (bands) {
@@ -65,6 +197,44 @@ public class IndirectLight {
return this;
}
/**
* Sets the irradiance from the radiance expressed as Spherical Harmonics.
*
* <p>The radiance must be specified as Spherical Harmonics coefficients L<sup>l,m</sup>, where
* each coefficient is comprised of three floats for red, green and blue components, respectively</p>
*
* <p>The index in the <code>sh</code> array is given by:
* <br><code>index(l, m) = 3 &times (l * (l + 1) + m)</code>
* <br><code>sh[index(l,m) + 0] = L<sub>R</sub><sup>l,m</sup></code>
* <br><code>sh[index(l,m) + 1] = L<sub>G</sub><sup>l,m</sup></code>
* <br><code>sh[index(l,m) + 2] = L<sub>B</sub><sup>l,m</sup></code>
* </p>
*
* <center>
* <table border="1" cellpadding="3">
* <tr><th> index </th><th> l </th><th> m </th>
* <tr align="right"><td>0</td><td>0</td><td> 0</td>
* <tr align="right"><td>1</td><td>1</td><td>-1</td>
* <tr align="right"><td>2</td><td>1</td><td> 0</td>
* <tr align="right"><td>3</td><td>1</td><td> 1</td>
* <tr align="right"><td>4</td><td>2</td><td>-2</td>
* <tr align="right"><td>5</td><td>2</td><td>-1</td>
* <tr align="right"><td>6</td><td>2</td><td> 0</td>
* <tr align="right"><td>7</td><td>2</td><td> 1</td>
* <tr align="right"><td>8</td><td>2</td><td> 2</td>
* </table>
* </center>
*
* @param bands Number of spherical harmonics bands. Must be 1, 2 or 3.
* @param sh Array containing the spherical harmonics coefficients.
* The size of the array must be 3 &times <code>bands<sup>2</sup></code>
* (i.e. 1, 4 or 9 <code>float3</code> coefficients respectively).
*
* @return This Builder, for chaining calls.
*
* @exception ArrayIndexOutOfBoundsException if the <code>sh</code> array length is smaller
* than 3 &times bands<sup>2</sup>
*/
@NonNull
public Builder radiance(@IntRange(from=1, to=3) int bands, @NonNull float[] sh) {
switch (bands) {
@@ -83,18 +253,52 @@ public class IndirectLight {
return this;
}
/**
* Sets the irradiance as a cubemap.
* <p></p>
* The irradiance can alternatively be specified as a cubemap instead of Spherical
* Harmonics coefficients. It may or may not be more efficient, depending on your
* hardware (essentially, it's trading ALU for bandwidth).
* <p></p>
* This irradiance cubemap can be generated with the <code>cmgen</code> tool.
*
* @param cubemap Cubemap representing the Irradiance pre-convolved by
* <code>&lt n &sdot l &gt</code>.
*
* @return This Builder, for chaining calls.
*
* @see #irradiance(int bands, float[] sh)
*/
@NonNull
public Builder irradiance(@NonNull Texture cubemap) {
nIrradianceAsTexture(mNativeBuilder, cubemap.getNativeObject());
return this;
}
/**
* Environment intensity (optional).
*
* <p>Because the environment is encoded usually relative to some reference, the
* range can be adjusted with this method.</p>
*
* @param envIntensity Scale factor applied to the environment and irradiance such that
* the result is in cd/m^2 (lux) units (default = 30000)
*
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder intensity(float envIntensity) {
nIntensity(mNativeBuilder, envIntensity);
return this;
}
/**
* Specifies the rigid-body transformation to apply to the IBL.
*
* @param rotation 3x3 rotation matrix. Must be a rigid-body transform.
*
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder rotation(@NonNull @Size(min = 9) float[] rotation) {
nRotation(mNativeBuilder,
@@ -104,6 +308,15 @@ public class IndirectLight {
return this;
}
/**
* Creates the IndirectLight object and returns a pointer to it.
*
* @param engine The {@link Engine} to associate this <code>IndirectLight</code> with.
*
* @return A newly created <code>IndirectLight</code>
*
* @exception IllegalStateException if a parameter to a builder function was invalid.
*/
@NonNull
public IndirectLight build(@NonNull Engine engine) {
long nativeIndirectLight = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -128,14 +341,31 @@ public class IndirectLight {
}
}
/**
* Sets the environment's intensity.
*
* <p>Because the environment is encoded usually relative to some reference, the
* range can be adjusted with this method.</p>
*
* @param intensity Scale factor applied to the environment and irradiance such that
* the result is in cd/m^2 units (default = 30000)
*/
public void setIntensity(float intensity) {
nSetIntensity(getNativeObject(), intensity);
}
/**
* Returns the environment's intensity in cd/m<sup>2</sup>.
*/
public float getIntensity() {
return nGetIntensity(getNativeObject());
}
/**
* Sets the rigid-body transformation to apply to the IBL.
*
* @param rotation 3x3 rotation matrix. Must be a rigid-body transform.
*/
public void setRotation(@NonNull @Size(min = 9) float[] rotation) {
Asserts.assertMat3fIn(rotation);
nSetRotation(getNativeObject(),
@@ -144,6 +374,14 @@ public class IndirectLight {
rotation[6], rotation[7], rotation[8]);
}
/**
* Returns the rigid-body transformation applied to the IBL.
*
* @param rotation an array of 9 floats to receive the rigid-body transformation applied to
* the IBL or <code>null</code>
* @return the <code>rotation</code> paramter if it was provided, or a newly allocated float
* array containing the rigid-body transformation applied to the IBL
*/
@NonNull @Size(min = 9)
public float[] getRotation(@Nullable @Size(min = 9) float[] rotation) {
rotation = Asserts.assertMat3f(rotation);
@@ -151,6 +389,27 @@ public class IndirectLight {
return rotation;
}
/**
* Helper to estimate the direction of the dominant light in the environment.
*
* <p>This assumes that there is only a single dominant light (such as the sun in outdoors
* environments), if it's not the case the direction returned will be an average of the
* various lights based on their intensity.</p>
*
* <p>If there are no clear dominant light, as is often the case with low dynamic range (LDR)
* environments, this method may return a wrong or unexpected direction.</p>
*
* <p>The dominant light direction can be used to set a directional light's direction,
* for instance to produce shadows that match the environment.</p>
*
* @param direction an array of 3 floats to receive a unit vector representing the direction of
* the dominant light or <code>null</code>
* @return the <code>direction</code> paramter if it was provided, or a newly allocated float
* array containing a unit vector representing the direction of the dominant light
*
* @see LightManager.Builder#direction
* @see #getColorEstimate
*/
@NonNull @Size(min = 3)
public float[] getDirectionEstimate(@Nullable @Size(min = 3) float[] direction) {
direction = Asserts.assertFloat3(direction);
@@ -158,6 +417,26 @@ public class IndirectLight {
return direction;
}
/**
* Helper to estimate the color and relative intensity of the environment in a given direction.
*
* <p>This can be used to set the color and intensity of a directional light. In this case
* make sure to multiply this relative intensity by the the intensity of this indirect light.</p>
*
* @param colorIntensity an array of 4 floats to receive the result or <code>null</code>
* @param x the x coordinate of a unit vector representing the direction of the light
* @param y the x coordinate of a unit vector representing the direction of the light
* @param z the x coordinate of a unit vector representing the direction of the light
*
* @return A vector of 4 floats where the first 3 components represent the linear color and
* the 4th component represents the intensity of the dominant light
*
* @see LightManager.Builder#color
* @see LightManager.Builder#intensity
* @see #getDirectionEstimate
* @see #getIntensity
* @see #setIntensity
*/
@NonNull @Size(min = 4)
public float[] getColorEstimate(@Nullable @Size(min = 4) float[] colorIntensity, float x, float y, float z) {
colorIntensity = Asserts.assertFloat4(colorIntensity);

View File

@@ -29,45 +29,157 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Set;
/**
* A Filament Material defines the visual appearance of an object. Materials function as a
* templates from which {@link MaterialInstance}s can be spawned. Use {@link Builder} to construct
* a Material object.
*
* @see <a href="https://google.github.io/filament/Materials.html">Filament Materials Guide</a>
*/
public class Material {
private long mNativeObject;
private final MaterialInstance mDefaultInstance;
private Set<VertexBuffer.VertexAttribute> mRequiredAttributes;
/** Supported shading models */
public enum Shading {
/**
* No lighting applied, emissive possible
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels/unlitmodel">
* Unlit model</a>
*/
UNLIT,
/**
* Default, standard lighting
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels/litmodel">
* Lit model</a>
*/
LIT,
/**
* Subsurface lighting model
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels/subsurfacemodel">
* Subsurface model</a>
*/
SUBSURFACE,
/**
* Cloth lighting model
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels/clothmodel">
* Cloth model</a>
*/
CLOTH,
/**
* Legacy lighting model
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels/specularglossiness">
* Specular glossiness</a>
*/
SPECULAR_GLOSSINESS
}
/**
* Attribute interpolation types in the fragment shader
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:interpolation">
* Vertex and attributes: interpolation</a>
*/
public enum Interpolation {
/** Default, smooth interpolation */
SMOOTH,
/** Flat interpolation */
FLAT
}
/**
* Supported blending modes
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:blending">
* Blending and transparency: blending</a>
*/
public enum BlendingMode {
/** Material is opaque. */
OPAQUE,
/**
* Material is transparent and color is alpha-pre-multiplied.
* Affects diffuse lighting only.
*/
TRANSPARENT,
/** Material is additive (e.g.: hologram). */
ADD,
MODULATE,
/** Material is masked (i.e. alpha tested). */
MASKED,
FADE
/**
* Material is transparent and color is alpha-pre-multiplied.
* Affects specular lighting.
*/
FADE,
/** Material darkens what's behind it. */
MULTIPLY,
/** Material brightens what's behind it. */
SCREEN,
}
/**
* Supported types of vertex domains
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:vertexdomain">
* Vertex and attributes: vertexDomain</a>
*/
public enum VertexDomain {
/** Vertices are in object space, default. */
OBJECT,
/** Vertices are in world space. */
WORLD,
/** Vertices are in view space. */
VIEW,
/** Vertices are in normalized device space. */
DEVICE
}
/**
* Face culling Mode
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
* Rasterization: culling</a>
*/
public enum CullingMode {
/** No culling. Front and back faces are visible. */
NONE,
/** Front face culling. Only back faces are visible. */
FRONT,
/** Back face culling. Only front faces are visible. */
BACK,
/** Front and back culling. Geometry is not visible. */
FRONT_AND_BACK
}
@@ -145,6 +257,13 @@ public class Material {
private Buffer mBuffer;
private int mSize;
/**
* Specifies the material data. The material data is a binary blob produced by
* libfilamat or by matc.
*
* @param buffer buffer containing material data
* @param size size of the material data in bytes
*/
@NonNull
public Builder payload(@NonNull Buffer buffer, @IntRange(from = 0) int size) {
mBuffer = buffer;
@@ -152,6 +271,15 @@ public class Material {
return this;
}
/**
* Creates and returns the Material object.
*
* @param engine reference to the Engine instance to associate this Material with
*
* @return the newly created object
*
* @exception IllegalStateException if the material could not be created
*/
@NonNull
public Material build(@NonNull Engine engine) {
long nativeMaterial = nBuilderBuild(engine.getNativeObject(), mBuffer, mSize);
@@ -160,6 +288,12 @@ public class Material {
}
}
/**
* Creates a new instance of this material. Material instances should be freed using
* {@link Engine#destroyMaterialInstance(MaterialInstance)}.
*
* @return the new instance
*/
@NonNull
public MaterialInstance createInstance() {
long nativeInstance = nCreateInstance(getNativeObject());
@@ -167,63 +301,163 @@ public class Material {
return new MaterialInstance(this, nativeInstance);
}
/** Returns the material's default instance. */
@NonNull
public MaterialInstance getDefaultInstance() {
return mDefaultInstance;
}
/**
* Returns the name of this material. The material name is used for debugging purposes.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:name">
* General: name</a>
*/
public String getName() {
return nGetName(getNativeObject());
}
/**
* Returns the shading model of this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialmodels">
* Material Models</a>
*/
public Shading getShading() {
return Shading.values()[nGetShading(getNativeObject())];
}
/**
* Returns the interpolation mode of this material. This affects how variables are interpolated.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:interpolation">
* Vertex and attributes: interpolation</a>
*/
public Interpolation getInterpolation() {
return Interpolation.values()[nGetInterpolation(getNativeObject())];
}
/**
* Returns the blending mode of this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:blending">
* Blending and transparency: blending</a>
*/
public BlendingMode getBlendingMode() {
return BlendingMode.values()[nGetBlendingMode(getNativeObject())];
}
/**
* Returns the vertex domain of this material.
*
* @se
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:vertexdomain">
* Vertex and attributes: vertexDomain</a>
* @return
*/
public VertexDomain getVertexDomain() {
return VertexDomain.values()[nGetVertexDomain(getNativeObject())];
}
/**
* Returns the default culling mode of this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
* Rasterization: culling</a>
*/
public CullingMode getCullingMode() {
return CullingMode.values()[nGetCullingMode(getNativeObject())];
}
/**
* Indicates whether this material will write to the color buffer.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:colorwrite">
* Rasterization: colorWrite</a>
*/
public boolean isColorWriteEnabled() {
return nIsColorWriteEnabled(getNativeObject());
}
/**
* Indicates whether this material will write to the depth buffer.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:depthwrite">
* Rasterization: depthWrite</a>
*/
public boolean isDepthWriteEnabled() {
return nIsDepthWriteEnabled(getNativeObject());
}
/**
* Indicates whether this material will use depth testing.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:depthculling">
* Rasterization: depthCulling</a>
*/
public boolean isDepthCullingEnabled() {
return nIsDepthCullingEnabled(getNativeObject());
}
/**
* Indicates whether this material is double-sided.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:doublesided">
* Rasterization: doubleSided</a>
*/
public boolean isDoubleSided() {
return nIsDoubleSided(getNativeObject());
}
/**
* Returns the alpha mask threshold used when the blending mode is set to masked.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:maskthreshold">
* Blending and transparency: maskThreshold</a>
*/
public float getMaskThreshold() {
return nGetMaskThreshold(getNativeObject());
}
/**
* Returns the screen-space variance for specular-antialiasing. This value is between 0 and 1.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance">
* Anti-aliasing: specularAntiAliasingVariance</a>
*/
public float getSpecularAntiAliasingVariance() {
return nGetSpecularAntiAliasingVariance(getNativeObject());
}
/**
* Returns the clamping threshold for specular-antialiasing. This value is between 0 and 1.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold">
* Anti-aliasing: specularAntiAliasingThreshold</a>
*/
public float getSpecularAntiAliasingThreshold() {
return nGetSpecularAntiAliasingThreshold(getNativeObject());
}
/**
* Returns a set of {@link VertexBuffer.VertexAttribute}s that are required by this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:requires">
* Vertex and attributes: requires</a>
*/
public Set<VertexBuffer.VertexAttribute> getRequiredAttributes() {
if (mRequiredAttributes == null) {
int bitSet = nGetRequiredAttributes(getNativeObject());
@@ -239,14 +473,38 @@ public class Material {
return mRequiredAttributes;
}
/**
* Returns a bit set representing the set of {@link VertexBuffer.VertexAttribute}s that are
* required by this material. Use {@link #getRequiredAttributes()} to get these as a Set object.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/vertexandattributes:requires">
* Vertex and attributes: requires</a>
*/
int getRequiredAttributesAsInt() {
return nGetRequiredAttributes(getNativeObject());
}
/**
* Returns the number of parameters declared by this material.
* The returned value can be 0.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
* General: parameters</a>
*/
public int getParameterCount() {
return nGetParameterCount(getNativeObject());
}
/**
* Returns a list of Parameter objects representing this material's parameters.
* The list may be empty if the material has no declared parameters.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
* General: parameters</a>
*/
public List<Parameter> getParameters() {
int count = getParameterCount();
List<Parameter> parameters = new ArrayList<>(count);
@@ -254,86 +512,308 @@ public class Material {
return parameters;
}
/**
* Indicates whether a parameter of the given name exists on this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
* General: parameters</a>
*/
public boolean hasParameter(@NonNull String name) {
return nHasParameter(getNativeObject(), name);
}
/**
* Sets the value of a bool parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, boolean x) {
mDefaultInstance.setParameter(name, x);
}
/**
* Sets the value of a float parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, float x) {
mDefaultInstance.setParameter(name, x);
}
/**
* Sets the value of an int parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, int x) {
mDefaultInstance.setParameter(name, x);
}
/**
* Sets the value of a bool2 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, boolean x, boolean y) {
mDefaultInstance.setParameter(name, x, y);
}
/**
* Sets the value of a float2 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, float x, float y) {
mDefaultInstance.setParameter(name, x, y);
}
/**
* Sets the value of an int2 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, int x, int y) {
mDefaultInstance.setParameter(name, x, y);
}
/**
* Sets the value of a bool3 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, boolean x, boolean y, boolean z) {
mDefaultInstance.setParameter(name, x, y, z);
}
/**
* Sets the value of a float3 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, float x, float y, float z) {
mDefaultInstance.setParameter(name, x, y, z);
}
/**
* Sets the value of a int3 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, int x, int y, int z) {
mDefaultInstance.setParameter(name, x, y, z);
}
/**
* Sets the value of a bool4 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, boolean x, boolean y, boolean z, boolean w) {
mDefaultInstance.setParameter(name, x, y, z, w);
}
/**
* Sets the value of a float4 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, float x, float y, float z, float w) {
mDefaultInstance.setParameter(name, x, y, z, w);
}
/**
* Sets the value of a int4 parameter on this material's default instance.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, int x, int y, int z, int w) {
mDefaultInstance.setParameter(name, x, y, z, w);
}
/**
* Set a bool parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 bool4s:
* <pre>{@code
* boolean[] a = new boolean[4 * 4];
* material.setDefaultParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* boolean[] a = new boolean[4 * 3];
* material.setDefaultParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 1, 3);
* }</pre>
* </p>
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name,
@NonNull MaterialInstance.BooleanElement type, @NonNull @Size(min = 1) boolean[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
mDefaultInstance.setParameter(name, type, v, offset, count);
}
/**
* Set an int parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 int4s:
* <pre>{@code
* int[] a = new int[4 * 4];
* material.setDefaultParameter("param", MaterialInstance.IntElement.INT4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* int[] a = new int[4 * 3];
* material.setDefaultParameter("param", MaterialInstance.IntElement.INT4, a, 1, 3);
* }</pre>
* </p>
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name,
@NonNull MaterialInstance.IntElement type, @NonNull @Size(min = 1) int[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
mDefaultInstance.setParameter(name, type, v, offset, count);
}
/**
* Set a float parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 float4s:
* <pre>{@code
* float[] a = new float[4 * 4];
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* float[] a = new float[4 * 3];
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 1, 3);
* }</pre>
* </p>
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name,
@NonNull MaterialInstance.FloatElement type, @NonNull @Size(min = 1) float[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
mDefaultInstance.setParameter(name, type, v, offset, count);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name the name of the material color parameter
* @param type whether the color is specified in the linear or sRGB space
* @param r red component
* @param g green component
* @param b blue component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, @NonNull Colors.RgbType type,
float r, float g, float b) {
mDefaultInstance.setParameter(name, type, r, g, b);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name the name of the material color parameter
* @param type whether the color is specified in the linear or sRGB space
* @param r red component
* @param g green component
* @param b blue component
* @param a alpha component
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name, @NonNull Colors.RgbaType type,
float r, float g, float b, float a) {
mDefaultInstance.setParameter(name, type, r, g, b, a);
}
/**
* Sets a texture and sampler parameter on this material's default instance.
*
* @param name The name of the material texture parameter
* @param texture The texture to set as parameter
* @param sampler The sampler to be used with this texture
*
* @see Material#getDefaultInstance()
*/
public void setDefaultParameter(@NonNull String name,
@NonNull Texture texture, @NonNull TextureSampler sampler) {
mDefaultInstance.setParameter(name, texture, sampler);

View File

@@ -58,6 +58,7 @@ public class MaterialInstance {
mNativeObject = nativeMaterialInstance;
}
/** @return the {@link Material} associated with this instance */
@NonNull
public Material getMaterial() {
if (mMaterial == null) {
@@ -66,118 +67,365 @@ public class MaterialInstance {
return mMaterial;
}
/**
* Sets the value of a bool parameter.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*/
public void setParameter(@NonNull String name, boolean x) {
nSetParameterBool(getNativeObject(), name, x);
}
/**
* Sets the value of a float parameter.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*/
public void setParameter(@NonNull String name, float x) {
nSetParameterFloat(getNativeObject(), name, x);
}
/**
* Sets the value of an int parameter.
*
* @param name the name of the material parameter
* @param x the value of the material parameter
*/
public void setParameter(@NonNull String name, int x) {
nSetParameterInt(getNativeObject(), name, x);
}
/**
* Sets the value of a bool2 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*/
public void setParameter(@NonNull String name, boolean x, boolean y) {
nSetParameterBool2(getNativeObject(), name, x, y);
}
/**
* Sets the value of a float2 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*/
public void setParameter(@NonNull String name, float x, float y) {
nSetParameterFloat2(getNativeObject(), name, x, y);
}
/**
* Sets the value of an int2 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
*/
public void setParameter(@NonNull String name, int x, int y) {
nSetParameterInt2(getNativeObject(), name, x, y);
}
/**
* Sets the value of a bool3 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*/
public void setParameter(@NonNull String name, boolean x, boolean y, boolean z) {
nSetParameterBool3(getNativeObject(), name, x, y, z);
}
/**
* Sets the value of a float3 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*/
public void setParameter(@NonNull String name, float x, float y, float z) {
nSetParameterFloat3(getNativeObject(), name, x, y, z);
}
/**
* Sets the value of a int3 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
*/
public void setParameter(@NonNull String name, int x, int y, int z) {
nSetParameterInt3(getNativeObject(), name, x, y, z);
}
/**
* Sets the value of a bool4 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*/
public void setParameter(@NonNull String name, boolean x, boolean y, boolean z, boolean w) {
nSetParameterBool4(getNativeObject(), name, x, y, z, w);
}
/**
* Sets the value of a float4 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*/
public void setParameter(@NonNull String name, float x, float y, float z, float w) {
nSetParameterFloat4(getNativeObject(), name, x, y, z, w);
}
/**
* Sets the value of a int4 parameter.
*
* @param name the name of the material parameter
* @param x the value of the first component
* @param y the value of the second component
* @param z the value of the third component
* @param w the value of the fourth component
*/
public void setParameter(@NonNull String name, int x, int y, int z, int w) {
nSetParameterInt4(getNativeObject(), name, x, y, z, w);
}
/**
* Sets a texture and sampler parameter on this material's default instance.
*
* @param name The name of the material texture parameter
* @param texture The texture to set as parameter
* @param sampler The sampler to be used with this texture
*/
public void setParameter(@NonNull String name,
@NonNull Texture texture, @NonNull TextureSampler sampler) {
nSetParameterTexture(getNativeObject(), name, texture.getNativeObject(), sampler.mSampler);
}
/**
* Set a bool parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 bool4s:
* <pre>{@code
* boolean[] a = new boolean[4 * 4];
* instance.setParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* boolean[] a = new boolean[4 * 3];
* instance.setParameter("param", MaterialInstance.BooleanElement.BOOL4, a, 1, 3);
* }</pre>
* </p>
*/
public void setParameter(@NonNull String name,
@NonNull BooleanElement type, @NonNull boolean[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
nSetBooleanParameterArray(getNativeObject(), name, type.ordinal(), v, offset, count);
}
/**
* Set an int parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 int4s:
* <pre>{@code
* int[] a = new int[4 * 4];
* instance.setParameter("param", MaterialInstance.IntElement.INT4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* int[] a = new int[4 * 3];
* instance.setParameter("param", MaterialInstance.IntElement.INT4, a, 1, 3);
* }</pre>
* </p>
*/
public void setParameter(@NonNull String name,
@NonNull IntElement type, @NonNull int[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
nSetIntParameterArray(getNativeObject(), name, type.ordinal(), v, offset, count);
}
/**
* Set a float parameter array by name.
*
* @param name name of the parameter array as defined by this Material
* @param type the number of components for each individual parameter
* @param v array of values to set to the named parameter array
* @param offset the number of elements to skip
* @param count the number of elements in the parameter array to set
*
* <p>For example, to set a parameter array of 4 float4s:
* <pre>{@code
* float[] a = new float[4 * 4];
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 0, 4);
* }</pre>
* To only set the last 3 elements, specify an offset of 1 and a count of 3:
* <pre>{@code
* float[] a = new float[4 * 3];
* material.setDefaultParameter("param", MaterialInstance.FloatElement.FLOAT4, a, 1, 3);
* }</pre>
* </p>
*/
public void setParameter(@NonNull String name,
@NonNull FloatElement type, @NonNull float[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count) {
nSetFloatParameterArray(getNativeObject(), name, type.ordinal(), v, offset, count);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name the name of the material color parameter
* @param type whether the color is specified in the linear or sRGB space
* @param r red component
* @param g green component
* @param b blue component
*/
public void setParameter(@NonNull String name, @NonNull Colors.RgbType type,
float r, float g, float b) {
float[] color = Colors.toLinear(type, r, g, b);
nSetParameterFloat3(getNativeObject(), name, color[0], color[1], color[2]);
}
/**
* Sets the color of the given parameter on this material's default instance.
*
* @param name the name of the material color parameter
* @param type whether the color is specified in the linear or sRGB space
* @param r red component
* @param g green component
* @param b blue component
* @param a alpha component
*/
public void setParameter(@NonNull String name, @NonNull Colors.RgbaType type,
float r, float g, float b, float a) {
float[] color = Colors.toLinear(type, r, g, b, a);
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
}
/**
* Set up a custom scissor rectangle; by default this encompasses the View.
*
* @param left left coordinate of the scissor box
* @param bottom bottom coordinate of the scissor box
* @param width width of the scissor box
* @param height height of the scissor box
*/
public void setScissor(@IntRange(from = 0) int left, @IntRange(from = 0) int bottom,
@IntRange(from = 0) int width, @IntRange(from = 0) int height) {
nSetScissor(getNativeObject(), left, bottom, width, height);
}
/** Returns the scissor rectangle to its default setting, which encompasses the View. */
public void unsetScissor() {
nUnsetScissor(getNativeObject());
}
/**
* Sets a polygon offset that will be applied to all renderables drawn with this material
* instance.
*
* The value of the offset is scale * dz + r * constant, where dz is the change in depth
* relative to the screen area of the triangle, and r is the smallest value that is guaranteed
* to produce a resolvable offset for a given implementation. This offset is added before the
* depth test.
*
* @warning using a polygon offset other than zero has a significant negative performance
* impact, as most implementations have to disable early depth culling. DO NOT USE unless
* absolutely necessary.
*
* @param scale scale factor used to create a variable depth offset for each triangle
* @param constant scale factor used to create a constant depth offset for each triangle
*/
public void setPolygonOffset(float scale, float constant) {
nSetPolygonOffset(getNativeObject(), scale, constant);
}
/**
* Overrides the minimum alpha value a fragment must have to not be discarded when the blend
* mode is MASKED. Defaults to 0.4 if it has not been set in the parent Material. The specified
* value should be between 0 and 1 and will be clamped if necessary.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:maskthreshold">
* Blending and transparency: maskThreshold</a>
*/
public void setMaskThreshold(float threshold) {
nSetMaskThreshold(getNativeObject(), threshold);
}
/**
* Sets the screen space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between
* 0 and 1 and will be clamped if necessary.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance">
* Anti-aliasing: specularAntiAliasingVariance</a>
*/
public void setSpecularAntiAliasingVariance(float variance) {
nSetSpecularAntiAliasingVariance(getNativeObject(), variance);
}
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
* and 1 and will be clamped if necessary.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold">
* Anti-aliasing: specularAntiAliasingThreshold</a>
*/
public void setSpecularAntiAliasingThreshold(float threshold) {
nSetSpecularAntiAliasingThreshold(getNativeObject(), threshold);
}
/**
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
* automatically disabled.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:doublesided">
* Rasterization: doubleSided</a>
*/
public void setDoubleSided(boolean doubleSided) {
nSetDoubleSided(getNativeObject(), doubleSided);
}
/**
* Overrides the default triangle culling state that was set on the material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
* Rasterization: culling</a>
*/
public void setCullingMode(Material.CullingMode mode) {
nSetCullingMode(getNativeObject(), mode.ordinal());
}

View File

@@ -23,6 +23,29 @@ import android.support.annotation.Size;
public final class MathUtils {
private MathUtils() { }
/**
* Packs the tangent frame represented by the specified tangent, bitangent, and normal into a
* quaternion.
*
* <p>
* Reflection is preserved by encoding it as the sign of the w component in the resulting
* quaternion. Since -0 cannot always be represented on the GPU, this function computes a bias
* to ensure values are always either positive or negative, never 0. The bias is computed based
* on a per-element storage size of 2 bytes, making the resulting quaternion suitable for
* storage into an SNORM16 vector.
* </p>
*
* @param tangentX the X component of the tangent
* @param tangentY the Y component of the tangent
* @param tangentZ the Z component of the tangent
* @param bitangentX the X component of the bitangent
* @param bitangentY the Y component of the bitangent
* @param bitangentZ the Z component of the bitangent
* @param normalX the X component of the normal
* @param normalY the Y component of the normal
* @param normalZ the Z component of the normal
* @param quaternion a float array of at least size 4 for the quaternion result to be stored
*/
public static void packTangentFrame(
float tangentX, float tangentY, float tangentZ,
float bitangentX, float bitangentY, float bitangentZ,
@@ -34,6 +57,30 @@ public final class MathUtils {
normalX, normalY, normalZ, quaternion, 0);
}
/**
* Packs the tangent frame represented by the specified tangent, bitangent, and normal into a
* quaternion.
*
* <p>
* Reflection is preserved by encoding it as the sign of the w component in the resulting
* quaternion. Since -0 cannot always be represented on the GPU, this function computes a bias
* to ensure values are always either positive or negative, never 0. The bias is computed based
* on a per-element storage size of 2 bytes, making the resulting quaternion suitable for
* storage into an SNORM16 vector.
* </p>
*
* @param tangentX the X component of the tangent
* @param tangentY the Y component of the tangent
* @param tangentZ the Z component of the tangent
* @param bitangentX the X component of the bitangent
* @param bitangentY the Y component of the bitangent
* @param bitangentZ the Z component of the bitangent
* @param normalX the X component of the normal
* @param normalY the Y component of the normal
* @param normalZ the Z component of the normal
* @param quaternion a float array of at least size 4 for the quaternion result to be stored
* @param offset offset, in elements, into the quaternion array to store the results
*/
public static void packTangentFrame(
float tangentX, float tangentY, float tangentZ,
float bitangentX, float bitangentY, float bitangentZ,

View File

@@ -20,6 +20,16 @@ import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* An offscreen render target that can be associated with a {@link View} and contains
* weak references to a set of attached {@link Texture} objects.
*
* <p>
* Clients are responsible for the lifetime of all associated <code>Texture</code> attachments.
* </p>
*
* @see View
*/
public class RenderTarget {
private long mNativeObject;
private final Texture[] mTextures = new Texture[2];
@@ -37,11 +47,17 @@ public class RenderTarget {
return mNativeObject;
}
/**
* An attachment point is a slot that can be assigned to a {@link Texture}.
*/
public enum AttachmentPoint {
COLOR,
DEPTH,
}
/**
* Constructs <code>RenderTarget</code> objects using a builder pattern.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final BuilderFinalizer mFinalizer;
@@ -53,6 +69,15 @@ public class RenderTarget {
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Sets a texture to a given attachment point.
*
* <p>All RenderTargets must have a non-null <code>COLOR</code> attachment.</p>
*
* @param attachment The attachment point of the texture.
* @param texture The associated texture object.
* @return A reference to this Builder for chaining calls.
*/
@NonNull
public Builder texture(@NonNull AttachmentPoint attachment, @Nullable Texture texture) {
mTextures[attachment.ordinal()] = texture;
@@ -60,24 +85,51 @@ public class RenderTarget {
return this;
}
/**
* Sets the mipmap level for a given attachment point.
*
* @param attachment The attachment point of the texture.
* @param level The associated mipmap level, 0 by default.
* @return A reference to this Builder for chaining calls.
*/
@NonNull
public Builder mipLevel(@NonNull AttachmentPoint attachment, @IntRange(from = 0) int level) {
nBuilderMipLevel(mNativeBuilder, attachment.ordinal(), level);
return this;
}
/**
* Sets the cubemap face for a given attachment point.
*
* @param attachment The attachment point.
* @param face The associated cubemap face.
* @return A reference to this Builder for chaining calls.
*/
@NonNull
public Builder face(@NonNull AttachmentPoint attachment, Texture.CubemapFace face) {
nBuilderFace(mNativeBuilder, attachment.ordinal(), face.ordinal());
return this;
}
/**
* Sets the layer for a given attachment point (for 3D textures).
*
* @param attachment The attachment point.
* @param layer The associated cubemap layer.
* @return A reference to this Builder for chaining calls.
*/
@NonNull
public Builder layer(@NonNull AttachmentPoint attachment, @IntRange(from = 0) int layer) {
nBuilderLayer(mNativeBuilder, attachment.ordinal(), layer);
return this;
}
/**
* Creates the RenderTarget object and returns a pointer to it.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*/
@NonNull
public RenderTarget build(@NonNull Engine engine) {
long nativeRenderTarget = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -105,20 +157,45 @@ public class RenderTarget {
}
}
/**
* Gets the texture set on the given attachment point.
*
* @param attachment Attachment point
* @return A Texture object or nullptr if no texture is set for this attachment point
*/
@Nullable
public Texture getTexture(@NonNull AttachmentPoint attachment) {
return mTextures[attachment.ordinal()];
}
/**
* Returns the mipmap level set on the given attachment point.
*
* @param attachment Attachment point
* @return the mipmap level set on the given attachment point
*/
@IntRange(from = 0)
public int getMipLevel(@NonNull AttachmentPoint attachment) {
return nGetMipLevel(getNativeObject(), attachment.ordinal());
}
/**
* Returns the face of a cubemap set on the given attachment point.
*
* @param attachment Attachment point
* @return A cubemap face identifier. This is only relevant if the attachment's texture is
* a cubemap.
*/
public Texture.CubemapFace getFace(AttachmentPoint attachment) {
return Texture.CubemapFace.values()[nGetFace(getNativeObject(), attachment.ordinal())];
}
/**
* Returns the texture-layer set on the given attachment point.
*
* @param attachment Attachment point
* @return A texture layer. This is only relevant if the attachment's texture is a 3D texture.
*/
@IntRange(from = 0)
public int getLayer(@NonNull AttachmentPoint attachment) {
return nGetLayer(getNativeObject(), attachment.ordinal());

View File

@@ -23,12 +23,51 @@ import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ReadOnlyBufferException;
/**
* A <code>Renderer</code> instance represents an operating system's window.
*
* <p>
* Typically, applications create a <code>Renderer</code> per window. The <code>Renderer</code> generates
* drawing commands for the render thread and manages frame latency.
* <br>
* A Renderer generates drawing commands from a View, itself containing a Scene description.
* </p>
*
* <h1>Creation and Destruction</h1>
*
* <p>A <code>Renderer</code> is created using {@link Engine#createRenderer} and destroyed
* using {@link Engine#destroyRenderer}.</p>
*
* @see Engine
* @see View
*/
public class Renderer {
private final Engine mEngine;
private long mNativeObject;
/**
* Indicates that the <code>dstSwapChain</code> passed into {@link #copyFrame} should be
* committed after the frame has been copied.
*
* @see #copyFrame
*/
public static final int MIRROR_FRAME_FLAG_COMMIT = 0x1;
/**
* Indicates that the presentation time should be set on the <code>dstSwapChain</code>
* passed into {@link #copyFrame} to the monotonic clock time when the frame is
* copied.
*
* @see #copyFrame
*/
public static final int MIRROR_FRAME_FLAG_SET_PRESENTATION_TIME = 0x2;
/**
* Indicates that the <code>dstSwapChain</code> passed into {@link #copyFrame} should be
* cleared to black before the frame is copied into the specified viewport.
*
* @see #copyFrame
*/
public static final int MIRROR_FRAME_FLAG_CLEAR = 0x4;
Renderer(@NonNull Engine engine, long nativeRenderer) {
@@ -36,25 +75,128 @@ public class Renderer {
mNativeObject = nativeRenderer;
}
/**
* Gets the {@link Engine} that created this <code>Renderer</code>.
*
* @return {@link Engine} instance this <code>Renderer</code> is associated to.
*/
@NonNull
public Engine getEngine() {
return mEngine;
}
/**
* Sets up a frame for this <code>Renderer</code>.
* <p><code>beginFrame</code> manages frame pacing, and returns whether or not a frame should be
* drawn. The goal of this is to skip frames when the GPU falls behind in order to keep the frame
* latency low.</p>
*
* <p>If a given frame takes too much time in the GPU, the CPU will get ahead of the GPU. The
* display will draw the same frame twice producing a stutter. At this point, the CPU is
* ahead of the GPU and depending on how many frames are buffered, latency increases.
* beginFrame() attempts to detect this situation and returns <code>false</code> in that case,
* indicating to the caller to skip the current frame.</p>
*
* <p>All calls to render() must happen <b>after</b> beginFrame().</p>
*
* @param swapChain the {@link SwapChain} instance to use
*
* @return <code>false</code> if the current frame must be skipped<br>
* When skipping a frame, the whole frame is canceled, and {@link #endFrame} must not
* be called.
*
* @see #endFrame
* @see #render
*/
public boolean beginFrame(@NonNull SwapChain swapChain) {
return nBeginFrame(getNativeObject(), swapChain.getNativeObject());
}
/**
* Finishes the current frame and schedules it for display.
* <p>
* <code>endFrame()</code> schedules the current frame to be displayed on the
* <code>Renderer</code>'s window.
* </p>
*
* <br><p>All calls to render() must happen <b>before</b> endFrame().</p>
*
* @see #beginFrame
* @see #render
*/
public void endFrame() {
nEndFrame(getNativeObject());
}
/**
* Renders a {@link View} into this <code>Renderer</code>'s window.
*
* <p>
* This is filament's main rendering method, most of the CPU-side heavy lifting is performed
* here. The purpose of the <code>render()</code> function is to generate render commands which
* are asynchronously executed by the {@link Engine}'s render thread.
* </p>
*
* <p><code>render()</code> generates commands for each of the following stages:</p>
* <ul>
* <li>Shadow map pass, if needed (currently only a single shadow map is supported)</li>
* <li>Depth pre-pass</li>
* <li>SSAO pass, if enabled</li>
* <li>Color pass</li>
* <li>Post-processing pass</li>
* </ul>
*
* A typical render loop looks like this:
*
* <pre>
* void renderLoop(Renderer renderer, SwapChain swapChain) {
* do {
* // typically we wait for VSYNC and user input events
* if (renderer.beginFrame(swapChain)) {
* renderer.render(mView);
* renderer.endFrame();
* }
* } while (!quit());
* }
* </pre>
*
* <ul>
*<li><code>render()</code> must be called <b>after</b> {@link #beginFrame} and <b>before</b>
*{@link #endFrame}.</li>
*
*<li><code>render()</code> must be called from the {@link Engine}'s main thread
*(or external synchronization must be provided). In particular, calls to <code>render()</code>
*on different <code>Renderer</code> instances <b>must</b> be synchronized.</li>
*
*<li><code>render()</code> performs potentially heavy computations and cannot be multi-threaded.
*However, internally, it is highly multi-threaded to both improve performance and mitigate
*the call's latency.</li>
*
*<li><code>render()</code> is typically called once per frame (but not necessarily).</li>
* </ul>
*
* @param view the {@link View} to render
*
* @see #beginFrame
* @see #endFrame
* @see View
*
*/
public void render(@NonNull View view) {
nRender(getNativeObject(), view.getNativeObject());
}
/**
* This method MUST be called before endFrame.
* Copies the currently rendered {@link View} to the indicated {@link SwapChain}, using the
* indicated source and destination rectangle.
*
* <p><code>copyFrame()</code> should be called after a frame is rendered using {@link #render}
* but before {@link #endFrame} is called.</p>
*
* @param dstSwapChain the {@link SwapChain} into which the frame should be copied
* @param dstViewport the destination rectangle in which to draw the view
* @param srcViewport the source rectangle to be copied
* @param flags one or more <code>CopyFrameFlag</code> behavior configuration flags
*/
public void copyFrame(
@NonNull SwapChain dstSwapChain, @NonNull Viewport dstViewport,
@@ -73,7 +215,68 @@ public class Renderer {
}
/**
* This method MUST be called before endFrame.
* Reads back the content of the {@link SwapChain} associated with this <code>Renderer</code>.
*
*<pre>
*
* Framebuffer as seen on User buffer (PixelBufferDescriptor)
* screen
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
*</pre>
*
*
* <p>Typically <code>readPixels</code> will be called after {@link #render} and before
* {@link #endFrame}.</p>
* <br>
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.
* Typically, this will happen after multiple calls to {@link #beginFrame},
* {@link #render}, {@link #endFrame}.</p>
* <br>
* <p><code>readPixels</code> is intended for debugging and testing.
* It will impact performance significantly.</p>
*
* @param xoffset left offset of the sub-region to read back
* @param yoffset bottom offset of the sub-region to read back
* @param width width of the sub-region to read back
* @param height height of the sub-region to read back
* @param buffer client-side buffer where the read-back will be written
*
* <p>
* The following format are always supported:
* <li>{@link Texture.Format#RGBA}</li>
* <li>{@link Texture.Format#RGBA_INTEGER}</li>
* </p>
*
* <p>
* The following types are always supported:
* <li>{@link Texture.Type#UBYTE}</li>
* <li>{@link Texture.Type#UINT}</li>
* <li>{@link Texture.Type#INT}</li>
* <li>{@link Texture.Type#FLOAT}</li>
* </p>
*
* <p>Other combination of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.</p>
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*/
public void readPixels(
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@@ -96,11 +299,67 @@ public class Renderer {
}
}
double getUserTime() {
/**
* Returns a timestamp (in seconds) for the last call to {@link #beginFrame}. This value is
* constant for all {@link View views} rendered during a frame. The epoch is set with
* {@link #resetUserTime}.
* <br>
* <p>In materials, this value can be queried using <code>vec4 getUserTime()</code>. The value
* returned is a <code>highp vec4</code> encoded as follows:</p>
* <pre>
* time.x = (float)Renderer.getUserTime();
* time.y = Renderer.getUserTime() - time.x;
* </pre>
*
* It follows that the following invariants are true:
* <pre>
* (double)time.x + (double)time.y == Renderer.getUserTime()
* time.x == (float)Renderer.getUserTime()
* </pre>
*
* This encoding allows the shader code to perform high precision (i.e. double) time
* calculations when needed despite the lack of double precision in the shader, e.g.:
* <br>
* To compute <code>(double)time * vertex</code> in the material, use the following construct:
* <pre>
* vec3 result = time.x * vertex + time.y * vertex;
* </pre>
*
* Most of the time, high precision computations are not required, but be aware that the
* precision of <code>time.x</code> rapidly diminishes as time passes:
*
* <center>
* <table border="1">
* <tr align="center"><th> time </th><th> precision </th></tr>
* <tr align="center"><td> 16.7s </td><td> us </td></tr>
* <tr align="center"><td> 4h39.7s </td><td> ms </td></tr>
* <tr align="center"><td> 77h </td><td> 1/60s </td></tr>
* </table>
* </center>
* <p>
*
* In other words, it is only possible to get microsecond accuracy for about 16s or millisecond
* accuracy for just under 5h. This problem can be mitigated by calling {@link #resetUserTime},
* or using high precision time as described above.
*
* @return the time in seconds since {@link #resetUserTime} was last called
*
* @see #resetUserTime
*/
public double getUserTime() {
return nGetUserTime(getNativeObject());
}
void resetUserTime() {
/**
* Sets the user time epoch to now, i.e. resets the user time to zero.
* <br>
* <p>Use this method used to keep the precision of time high in materials, in practice it should
* be called at least when the application is paused, e.g.
* {@link android.app.Activity#onPause() Activity.onPause} in Android.</p>
*
* @see #getUserTime
*/
public void resetUserTime() {
nResetUserTime(getNativeObject());
}

View File

@@ -18,6 +18,28 @@ package com.google.android.filament;
import android.support.annotation.Nullable;
/**
* A <code>Scene</code> is a flat container of {@link RenderableManager} and {@link LightManager}
* components.
* <br>
* <p>A <code>Scene</code> doesn't provide a hierarchy of objects, i.e.: it's not a scene-graph.
* However, it manages the list of objects to render and the list of lights. These can
* be added or removed from a <code>Scene</code> at any time.
* Moreover clients can use {@link TransformManager} to create a graph of transforms.</p>
* <br>
* <p>A {@link RenderableManager} component <b>must</b> be added to a <code>Scene</code> in order
* to be rendered, and the <code>Scene</code> must be provided to a {@link View}.</p>
*
* <h1>Creation and Destruction</h1>
*
* A <code>Scene</code> is created using {@link Engine#createScene} and destroyed using
* {@link Engine#destroyScene(Scene)}.
*
* @see View
* @see LightManager
* @see RenderableManager
* @see TransformManager
*/
public class Scene {
private long mNativeObject;
private @Nullable Skybox mSkybox;
@@ -27,35 +49,78 @@ public class Scene {
mNativeObject = nativeScene;
}
/**
* @return the {@link Skybox} or <code>null</code> if none is set
* @see #setSkybox(Skybox)
*/
@Nullable
public Skybox getSkybox() {
return mSkybox;
}
/**
* Sets the {@link Skybox}.
*
* The {@link Skybox} is drawn last and covers all pixels not touched by geometry.
*
* @param skybox the {@link Skybox} to use to fill untouched pixels,
* or <code>null</code> to unset the {@link Skybox}.
*/
public void setSkybox(@Nullable Skybox skybox) {
mSkybox = skybox;
nSetSkybox(getNativeObject(), mSkybox != null ? mSkybox.getNativeObject() : 0);
}
/**
* @return the {@link IndirectLight} or <code>null</code> if none is set
* @see #setIndirectLight(IndirectLight)
*/
@Nullable
public IndirectLight getIndirectLight() {
return mIndirectLight;
}
/**
* Sets the {@link IndirectLight} to use when rendering the <code>Scene</code>.
*
* Currently, a <code>Scene</code> may only have a single {@link IndirectLight}.
* This call replaces the current {@link IndirectLight}.
*
* @param ibl the {@link IndirectLight} to use when rendering the <code>Scene</code>
* or <code>null</code> to unset.
*/
public void setIndirectLight(@Nullable IndirectLight ibl) {
mIndirectLight = ibl;
nSetIndirectLight(getNativeObject(),
mIndirectLight != null ? mIndirectLight.getNativeObject() : 0);
}
/**
* Adds an {@link Entity} to the <code>Scene</code>.
*
* @param entity the entity is ignored if it doesn't have a {@link RenderableManager} component
* or {@link LightManager} component.<br>
* A given {@link Entity} object can only be added once to a <code>Scene</code>.
*/
public void addEntity(@Entity int entity) {
nAddEntity(getNativeObject(), entity);
}
/**
* Adds a list of entities to the <code>Scene</code>.
*
* @param entities array containing entities to add to the <code>Scene</code>.
*/
public void addEntities(@Entity int[] entities) {
nAddEntities(getNativeObject(), entities);
}
/**
* Removes an {@link Entity} from the <code>Scene</code>.
*
* @param entity the {@link Entity} to remove from the <code>Scene</code>. If the specified
* <code>entity</code> doesn't exist, this call is ignored.
*/
public void removeEntity(@Entity int entity) {
nRemove(getNativeObject(), entity);
}
@@ -67,10 +132,20 @@ public class Scene {
removeEntity(entity);
}
/**
* Returns the number of {@link RenderableManager} components in the <code>Scene</code>.
*
* @return number of {@link RenderableManager} components in the <code>Scene</code>..
*/
public int getRenderableCount() {
return nGetRenderableCount(getNativeObject());
}
/**
* Returns the number of {@link LightManager} components in the <code>Scene</code>.
*
* @return number of {@link LightManager} components in the <code>Scene</code>..
*/
public int getLightCount() {
return nGetLightCount(getNativeObject());
}

View File

@@ -21,6 +21,31 @@ import android.support.annotation.NonNull;
import com.google.android.filament.proguard.UsedByReflection;
/**
* Skybox
* <p>When added to a {@link Scene}, the <code>Skybox</code> fills all untouched pixels.</p>
*
* <h1>Creation and destruction</h1>
*
* A <code>Skybox</code> object is created using the {@link Skybox.Builder} and destroyed by calling
* {@link Engine#destroySkybox}.<br>
* <pre>
* Engine engine = Engine.create();
*
* Scene scene = engine.createScene();
*
* Skybox skybox = new Skybox.Builder()
* .environment(cubemap)
* .build(engine);
*
* scene.setSkybox(skybox);
* </pre>
*
* Currently only {@link Texture} based sky boxes are supported.
*
* @see Scene
* @see IndirectLight
*/
public class Skybox {
private long mNativeObject;
@@ -29,28 +54,88 @@ public class Skybox {
mNativeObject = nativeSkybox;
}
/**
* Use <code>Builder</code> to construct a <code>Skybox</code> object instance.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
/**
* Use <code>Builder</code> to construct a <code>Skybox</code> object instance.
*/
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Set the environment map (i.e. the skybox content).
*
* <p>The <code>Skybox</code> is rendered as though it were an infinitely large cube with the
* camera inside it. This means that the cubemap which is mapped onto the cube's exterior
* will appear mirrored. This follows the OpenGL conventions.</p>
*
* <p>The <code>cmgen</code> tool generates reflection maps by default which are therefore
* ideal to use as skyboxes.</p>
*
* @param cubemap A cubemap {@link Texture}
*
* @return This Builder, for chaining calls.
*
* @see Texture
*/
@NonNull
public Builder environment(@NonNull Texture texture) {
nBuilderEnvironment(mNativeBuilder, texture.getNativeObject());
public Builder environment(@NonNull Texture cubemap) {
nBuilderEnvironment(mNativeBuilder, cubemap.getNativeObject());
return this;
}
/**
* Indicates whether the sun should be rendered. The sun can only be
* rendered if there is at least one light of type {@link LightManager.Type#SUN} in
* the {@link Scene}. The default value is <code>false</code>.
*
* @param show <code>true</code> if the sun should be rendered, <code>false</code> otherwise
*
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder showSun(boolean show) {
nBuilderShowSun(mNativeBuilder, show);
return this;
}
/**
* Sets the <code>Skybox</code> intensity when no {@link IndirectLight} is set
*
* <p>This call is ignored when an {@link IndirectLight} is set, otherwise it is used in
* its place.</p>
*
* @param envIntensity Scale factor applied to the skybox texel values such that
* the result is in cd/m<sup>2</sup> (lux) units (default = 30000)
*
* @return This Builder, for chaining calls.
*
* @see IndirectLight.Builder#intensity
*/
@NonNull
public Builder intensity(float envIntensity) {
nBuilderIntensity(mNativeBuilder, envIntensity);
return this;
}
/**
* Creates a <code>Skybox</code> object
*
* @param engine the {@link Engine} to associate this <code>Skybox</code> with.
*
* @return A newly created <code>Skybox</code>object
*
* @exception IllegalStateException can be thrown if the <code>Skybox</code> couldn't be created
*/
@NonNull
public Skybox build(@NonNull Engine engine) {
long nativeSkybox = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -75,14 +160,35 @@ public class Skybox {
}
}
public void setLayerMask(@IntRange(from = 0, to = 255) int select, @IntRange(from = 0, to = 255) int value) {
nSetLayerMask(getNativeObject(), select & 0xff, value & 0xff);
/**
* Sets bits in a visibility mask. By default, this is <code>0x1</code>.
* <p>This provides a simple mechanism for hiding or showing this <code>Skybox</code> in a
* {@link Scene}.</p>
*
* <p>For example, to set bit 1 and reset bits 0 and 2 while leaving all other bits unaffected,
* call: <code>setLayerMask(7, 2)</code>.</p>
*
* @param select the set of bits to affect
* @param values the replacement values for the affected bits
*
* @see View#setVisibleLayers
*/
public void setLayerMask(@IntRange(from = 0, to = 255) int select, @IntRange(from = 0, to = 255) int values) {
nSetLayerMask(getNativeObject(), select & 0xff, values & 0xff);
}
/**
* @return the visibility mask bits
*/
public int getLayerMask() {
return nGetLayerMask(getNativeObject());
}
/**
* Returns the <code>Skybox</code>'s intensity in cd/m<sup>2</sup>.
*/
public float getIntensity() { return nGetIntensity(getNativeObject()); }
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed Skybox");
@@ -98,7 +204,9 @@ public class Skybox {
private static native void nDestroyBuilder(long nativeSkyboxBuilder);
private static native void nBuilderEnvironment(long nativeSkyboxBuilder, long nativeTexture);
private static native void nBuilderShowSun(long nativeSkyboxBuilder, boolean show);
private static native void nBuilderIntensity(long nativeSkyboxBuilder, float intensity);
private static native long nBuilderBuild(long nativeSkyboxBuilder, long nativeEngine);
private static native void nSetLayerMask(long nativeSkybox, int select, int value);
private static native int nGetLayerMask(long nativeSkybox);
private static native float nGetIntensity(long nativeSkybox);
}

View File

@@ -23,6 +23,12 @@ import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ReadOnlyBufferException;
/**
* <code>Stream</code> is used to attach a native video stream to a filament {@link Texture}.
*
* @see Texture#setExternalStream
* @see Engine#destroyStream
*/
public class Stream {
private long mNativeObject;
private long mNativeEngine;
@@ -32,20 +38,31 @@ public class Stream {
mNativeEngine = engine.getNativeObject();
}
/**
* Use <code>Builder</code> to construct an Stream object instance.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
/**
* Use <code>Builder</code> to construct an Stream object instance.
*/
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Accepted types for the stream source:
* - Android: SurfaceView
* - Other: none
* Creates a native stream. Native streams can sample data directly from an
* opaque platform object such as a {@link android.graphics.SurfaceTexture SurfaceTexture}
* on Android.
*
* @param streamSource an opaque native stream handle, e.g.: on Android this must be a
* {@link android.graphics.SurfaceTexture SurfaceTexture} object
* @return This Builder, for chaining calls.
* @see Texture#setExternalStream
*/
@NonNull
public Builder stream(@NonNull Object streamSource) {
@@ -56,24 +73,57 @@ public class Stream {
throw new IllegalArgumentException("Invalid stream source: " + streamSource);
}
/**
* Creates a copy stream. A copy stream will sample data from the supplied
* external texture and copy it into an internal private texture.
*
* <p>Currently only OpenGL external texture ids are supported.</p>
*
* @param externalTextureId An opaque texture id (typically a GLuint created with
* <code>glGenTextures()</code>) in a context shared with
* filament -- in that case this texture's target must be
* <code>GL_TEXTURE_EXTERNAL_OES.</code>
* @return This Builder, for chaining calls.
* @see Texture#setExternalStream
*/
@NonNull
public Builder stream(long externalTextureId) {
nBuilderStream(mNativeBuilder, externalTextureId);
return this;
}
/**
* @param width initial width of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* {@link #stream(long)}
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder width(int width) {
nBuilderWidth(mNativeBuilder, width);
return this;
}
/**
* @param height initial height of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* {@link #stream(long)}
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder height(int height) {
nBuilderHeight(mNativeBuilder, height);
return this;
}
/**
* Creates a new <code>Stream</code> object instance.
*
* @param engine {@link Engine} instance to associate this <code>Stream</code> with.
*
* @return newly created <code>Stream</code> object
* @exception IllegalStateException if the <code>Stream</code> couldn't be created
*/
@NonNull
public Stream build(@NonNull Engine engine) {
long nativeStream = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -99,14 +149,92 @@ public class Stream {
}
}
/**
* Indicates whether this <code>Stream</code> is a native stream or a copy stream.
*
* @return true if this is a native <code>Stream</code>, false otherwise.
*/
public boolean isNative() {
return nIsNative(getNativeObject());
}
/**
* Updates the size of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
* {@link Builder#stream(long)}
*
* @param width new width of the incoming stream
* @param height new height of the incoming stream
*/
public void setDimensions(@IntRange(from = 0) int width, @IntRange(from = 0) int height) {
nSetDimensions(getNativeObject(), width, height);
}
/**
* Reads back the content of the last frame of a <code>Stream</code> since the last call to
* {@link Renderer#beginFrame}.
*
* <p>The Stream must be a copy stream, which can be checked with {@link #isNative()}.
* This function is a no-op otherwise.</p>
*
* <pre>
*
* Stream buffer User buffer (PixelBufferDescriptor)
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* </pre>
*
* <p>Typically readPixels() will be called after {@link Renderer#beginFrame}.</p>
*
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.
* Typically, this will happen after multiple calls to {@link Renderer#beginFrame},
* {@link Renderer#render}, {@link Renderer#endFrame}.</p>
*
* <p><code>readPixels</code> is intended for debugging and testing.
* It will impact performance significantly.</p>
*
* @param xoffset left offset of the sub-region to read back
* @param yoffset bottom offset of the sub-region to read back
* @param width width of the sub-region to read back
* @param height height of the sub-region to read back
* @param buffer client-side buffer where the read-back will be written
*
* <p>
* The following format are always supported:
* <li>{@link Texture.Format#RGBA}</li>
* <li>{@link Texture.Format#RGBA_INTEGER}</li>
* </p>
*
* <p>
* The following types are always supported:
* <li>{@link Texture.Type#UBYTE}</li>
* <li>{@link Texture.Type#UINT}</li>
* <li>{@link Texture.Type#INT}</li>
* <li>{@link Texture.Type#FLOAT}</li>
* </p>
*
* <p>Other combination of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.</p>
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*/
public void readPixels(
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@IntRange(from = 0) int width, @IntRange(from = 0) int height,
@@ -128,6 +256,13 @@ public class Stream {
}
}
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*
* This value can change at any time.
*
* @return timestamp in nanosecond.
*/
public long getTimestamp() {
return nGetTimestamp(getNativeObject());
}

View File

@@ -18,12 +18,71 @@ package com.google.android.filament;
import android.support.annotation.NonNull;
/**
* A <code>SwapChain</code> represents an Operating System's <b>native</b> renderable surface.
*
* <p>Typically it's a native window or a view. Because a <code>SwapChain</code> is initialized
* from a native object, it is given to filament as an <code>Object</code>, which must be of the
* proper type for each platform filament is running on.</p>
*
* <code>
* SwapChain swapChain = engine.createSwapChain(nativeWindow);
* </code>
*
* <p>The <code>nativeWindow</code> parameter above must be of type:</p>
*
* <center>
* <table border="1">
* <tr><th> Platform </th><th> nativeWindow type </th></tr>
* <tr><td> Android </td><td>{@link android.view.Surface Surface}</td></tr>
* </table>
* </center>
* <p>
*
* <h1>Examples</h1>
*
* <h2>Android</h2>
*
*
* <p>A {@link android.view.Surface Surface} can be retrieved from a
* {@link android.view.SurfaceView SurfaceView} or {@link android.view.SurfaceHolder SurfaceHolder}
* easily using {@link android.view.SurfaceHolder#getSurface SurfaceHolder.getSurface()} and/or
* {@link android.view.SurfaceView#getHolder SurfaceView.getHolder()}.</p>
*
* <p>To use a {@link android.view.TextureView Textureview} as a <code>SwapChain</code>, it is
* necessary to first get its {@link android.graphics.SurfaceTexture SurfaceTexture},
* for instance using {@link android.view.TextureView.SurfaceTextureListener SurfaceTextureListener}
* and then create a {@link android.view.Surface Surface}:</p>
*
* <pre>
* // using a TextureView.SurfaceTextureListener:
* public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
* mSurface = new Surface(surfaceTexture);
* // mSurface can now be used with Engine.createSwapChain()
* }
* </pre>
*
* @see Engine
*/
public class SwapChain {
private final Object mSurface;
private long mNativeObject;
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
SwapChain(long nativeSwapChain, @NonNull Object surface) {
@@ -31,6 +90,9 @@ public class SwapChain {
mSurface = surface;
}
/**
* @return the native <code>Object</code> this <code>SwapChain</code> was created from.
*/
@NonNull
public Object getNativeWindow() {
return mSurface;

View File

@@ -29,6 +29,47 @@ import java.nio.ByteBuffer;
import static com.google.android.filament.Texture.Type.COMPRESSED;
/**
* Texture
* <p>The <code>Texture</code> class supports:</p>
* <ul>
* <li>2D textures</li>
* <li>3D textures</li>
* <li>Cube maps</li>
* <li>mip mapping</li>
* </ul>
*
*
* <h1>Usage example</h1>
*
* A <code>Texture</code> object is created using the {@link Texture.Builder} and destroyed by
* calling {@link Engine#destroyTexture}. They're bound using {@link MaterialInstance#setParameter}.
*
* <pre>
* Engine engine = Engine.create();
*
* Material material = new Material.Builder()
* .payload( ... )
* .build(ending);
*
* MaterialInstance mi = material.getDefaultInstance();
*
* Texture texture = new Texture.Builder()
* .width(64)
* .height(64)
* .build(engine);
*
*
* texture.setImage(engine, 0,
* new Texture.PixelBufferDescriptor( ... ));
*
* mi.setParameter("parameterName", texture, new TextureSampler());
* </pre>
*
* @see #setImage
* @see PixelBufferDescriptor
* @see MaterialInstance#setParameter(String, Texture, TextureSampler)
*/
public class Texture {
private long mNativeObject;
@@ -37,12 +78,95 @@ public class Texture {
mNativeObject = nativeTexture;
}
/**
* Type of sampler
*/
public enum Sampler {
/** 2D sampler */
SAMPLER_2D,
/** Cubemap sampler */
SAMPLER_CUBEMAP,
/** External texture sampler */
SAMPLER_EXTERNAL
}
/**
* Internal texel formats
*
* <p>These formats are used to specify a texture's internal storage format.</p>
*
* <h1>Enumerants syntax format</h1>
*
* <code>[components][size][type]</code>
* <br><code>components</code> : List of stored components by this format
* <br><code>size</code> : Size in bit of each component
* <br><code>type</code> : Type this format is stored as
*
* <center>
* <table border="1">
* <tr><th> Name </th><th> Component </th></tr>
* <tr><td> R </td><td> Linear Red </td></tr>
* <tr><td> RG </td><td> Linear Red, Green </td></tr>
* <tr><td> RGB </td><td> Linear Red, Green, Blue </td></tr>
* <tr><td> RGBA </td><td> Linear Red, Green Blue, Alpha </td></tr>
* <tr><td> SRGB </td><td> sRGB encoded Red, Green, Blue </td></tr>
* <tr><td> DEPTH </td><td> Depth </td></tr>
* <tr><td> STENCIL </td><td> Stencil </td></tr>
* </table>
* </center>
* <br>
*
* <center>
* <table border="1">
* <tr><th> Name </th><th> Type </th></tr>
* <tr><td> (none) </td><td> Unsigned Normalized Integer [0, 1] </th></tr>
* <tr><td> _SNORM </td><td> Signed Normalized Integer [-1, 1] </td></tr>
* <tr><td> UI </td><td> Unsigned Integer [0, 2<sup>size</sup>] </td></tr>
* <tr><td> I </td><td> Signed Integer [-2<sup>size-1</sup>, 2<sup>size-1</sup>-1] </td></tr>
* <tr><td> F </td><td> Floating-point </td></tr>
* </table>
* </center>
* <br>
*
* <h1>Special color formats</h1>
*
* There are a few special color formats that don't follow the convention above:
*
* <center>
* <table border="1">
* <tr><th> Name </th><th> Format </th></tr>
* <tr><td> RGB565 </td><td> 5-bits for R and B, 6-bits for G. </td></tr>
* <tr><td> RGB5_A1 </td><td> 5-bits for R, G and B, 1-bit for A. </td></tr>
* <tr><td> RGB10_A2 </td><td> 10-bits for R, G and B, 2-bits for A. </td></tr>
* <tr><td> RGB9_E5 </td><td> <b>Unsigned</b> floating point. 9-bits mantissa for RGB, 5-bits shared exponent </td></tr>
* <tr><td> R11F_G11F_B10F </td><td> <b>Unsigned</b> floating point. 6-bits mantissa, for R and G, 5-bits for B. 5-bits exponent. </td></tr>
* <tr><td> SRGB8_A8 </td><td> sRGB 8-bits with linear 8-bits alpha. </td></tr>
* <tr><td> DEPTH24_STENCIL8 </td><td> 24-bits unsigned normalized integer depth, 8-bits stencil. </td></tr>
* </table>
* </center>
* <br>
*
* <h1>Compressed texture formats</h1>
*
* Many compressed texture formats are supported as well, which include (but are not limited to)
* the following list:
*
* <center>
* <table border="1">
* <tr><th> Name </th><th> Format </th></tr>
* <tr><td> EAC_R11 </td><td> Compresses R11UI </td></tr>
* <tr><td> EAC_R11_SIGNED </td><td> Compresses R11I </td></tr>
* <tr><td> EAC_RG11 </td><td> Compresses RG11UI </td></tr>
* <tr><td> EAC_RG11_SIGNED </td><td> Compresses RG11I </td></tr>
* <tr><td> ETC2_RGB8 </td><td> Compresses RGB8 </td></tr>
* <tr><td> ETC2_SRGB8 </td><td> compresses SRGB8 </td></tr>
* <tr><td> ETC2_EAC_RGBA8 </td><td> Compresses RGBA8 </td></tr>
* <tr><td> ETC2_EAC_SRGBA8 </td><td> Compresses SRGB8_A8 </td></tr>
* <tr><td> ETC2_RGB8_A1 </td><td> Compresses RGB8 with 1-bit alpha </td></tr>
* <tr><td> ETC2_SRGB8_A1 </td><td> Compresses sRGB8 with 1-bit alpha </td></tr>
* </table>
* </center>
*/
public enum InternalFormat {
// 8-bits per element
R8, R8_SNORM, R8UI, R8I, STENCIL8,
@@ -92,6 +216,10 @@ public class Texture {
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA
}
/**
* Compressed data types for use with {@link PixelBufferDescriptor}
* @see InternalFormat
*/
public enum CompressedFormat {
// Mandatory in GLES 3.0 and GL 4.3
EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED,
@@ -103,15 +231,27 @@ public class Texture {
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA
}
/**
* Cubemap faces
*/
public enum CubemapFace {
/** +x face */
POSITIVE_X,
/** -x face */
NEGATIVE_X,
/** +y face */
POSITIVE_Y,
/** -y face */
NEGATIVE_Y,
/** +z face */
POSITIVE_Z,
/** -z face */
NEGATIVE_Z
}
/**
* Pixel color format
*/
public enum Format {
R,
R_INTEGER,
@@ -128,19 +268,43 @@ public class Texture {
ALPHA
}
/**
* Pixel data type
*/
public enum Type {
/** unsigned byte, 8-bits */
UBYTE,
/** signed byte, 8-bits */
BYTE,
/** unsigned short, 16-bits */
USHORT,
/** signed short, 16-bits */
SHORT,
/** unsigned int, 32-bits */
UINT,
/** signed int, 32-bits */
INT,
/** half-float, 16-bits float with 10 bits mantissa */
HALF,
/** float, 32-bits float, with 24 bits mantissa */
FLOAT,
/** a compessed type */
COMPRESSED,
/** unsigned 5.6 (5.5 for blue) float packed in 32-bits */
UINT_10F_11F_11F_REV
}
/**
* A descriptor to an image in main memory, typically used to transfer image data from the CPU
* to the GPU.
* <p>A <code>PixelBufferDescriptor</code> owns the memory buffer it references,
* therefore <code>PixelBufferDescriptor</code> cannot be copied, but can be moved.</p>
*
* <code>PixelBufferDescriptor</code> releases ownership of the memory-buffer when it's
* destroyed.
*
* @see #setImage
*/
public static class PixelBufferDescriptor {
public Buffer storage;
@@ -165,6 +329,20 @@ public class Texture {
* - Android: Handler, Executor
* - Other: Executor
*/
/**
* Creates a <code>PixelBufferDescriptor</code>
*
* @param storage CPU-side buffer containing the image data to upload into the texture
* @param format Pixel {@link Format format} of the CPU-side image
* @param type Pixel data {@link Type type} of the CPU-side image
* @param alignment Row-alignment in bytes of the CPU-side image (1 to 8 bytes)
* @param left Left coordinate in pixels of the CPU-side image
* @param top Top coordinate in pixels of the CPU-side image
* @param stride Stride in pixels of the CPU-side image (i.e. distance in pixels to the next row)
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback A callback executed by <code>handler</code> when <code>storage</code> is no longer needed.
*/
public PixelBufferDescriptor(@NonNull Buffer storage,
@NonNull Format format, @NonNull Type type,
@IntRange(from = 1, to = 8) int alignment,
@@ -182,17 +360,48 @@ public class Texture {
this.callback = callback;
}
/**
* Creates a <code>PixelBufferDescriptor</code> with some default values and no callback.
*
* @param storage CPU-side buffer containing the image data to upload into the texture
* @param format Pixel {@link Format format} of the CPU-side image
* @param type Pixel data {@link Type type} of the CPU-side image
*
* @see #setCallback
*/
public PixelBufferDescriptor(@NonNull Buffer storage,
@NonNull Format format, @NonNull Type type) {
this(storage, format, type, 1, 0, 0, 0, null, null);
}
/**
* Creates a <code>PixelBufferDescriptor</code> with some default values and no callback.
*
* @param storage CPU-side buffer containing the image data to upload into the texture
* @param format Pixel {@link Format format} of the CPU-side image
* @param type Pixel data {@link Type type} of the CPU-side image
* @param alignment Row-alignment in bytes of the CPU-side image (1 to 8 bytes)
*
* @see #setCallback
*/
public PixelBufferDescriptor(@NonNull Buffer storage,
@NonNull Format format, @NonNull Type type,
@IntRange(from = 1, to = 8) int alignment) {
this(storage, format, type, alignment, 0, 0, 0, null, null);
}
/**
* Creates a <code>PixelBufferDescriptor</code> with some default values and no callback.
*
* @param storage CPU-side buffer containing the image data to upload into the texture
* @param format Pixel {@link Format format} of the CPU-side image
* @param type Pixel data {@link Type type} of the CPU-side image
* @param alignment Row-alignment in bytes of the CPU-side image (1 to 8 bytes)
* @param left Left coordinate in pixels of the CPU-side image
* @param top Top coordinate in pixels of the CPU-side image
*
* @see #setCallback
*/
public PixelBufferDescriptor(@NonNull Buffer storage,
@NonNull Format format, @NonNull Type type,
@IntRange(from = 1, to = 8) int alignment,
@@ -200,6 +409,14 @@ public class Texture {
this(storage, format, type, alignment, left, top, 0, null, null);
}
/**
*
* @param storage CPU-side buffer containing the image data to upload into the texture
* @param format Compressed pixel {@link CompressedFormat format} of the CPU-side image
* @param compressedSizeInBytes Size of the compressed data in bytes
*
* @see #setCallback
*/
public PixelBufferDescriptor(@NonNull ByteBuffer storage,
@NonNull CompressedFormat format,
@IntRange(from = 0) int compressedSizeInBytes) {
@@ -211,9 +428,10 @@ public class Texture {
}
/**
* Valid handler types:
* - Android: Handler, Executor
* - Other: Executor
* Set or replace the callback called when the CPU-side data is no longer needed.
*
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback A callback executed by <code>handler</code> when <code>storage</code> is no longer needed.
*/
public void setCallback(@Nullable Object handler, @Nullable Runnable callback) {
this.handler = handler;
@@ -288,57 +506,109 @@ public class Texture {
}
}
/**
* Options of {@link #generatePrefilterMipmap}
*/
public static class PrefilterOptions {
/** number of samples for roughness pre-filtering */
public int sampleCount = 8;
/** whether to generate a reflection map (mirror) */
public boolean mirror = true;
}
/**
* Checks whether a given format is supported for texturing in this {@link Engine}.
* This depends on the selected backend.
*
* @param engine {@link Engine} to test the {@link InternalFormat InternalFormat} against
* @param format format to check
* @return <code>true</code> if this format is supported for texturing.
*/
public static boolean isTextureFormatSupported(@NonNull Engine engine,
@NonNull InternalFormat format) {
return nIsTextureFormatSupported(engine.getNativeObject(), format.ordinal());
}
/**
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
// Keep to finalize native resources
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
/**
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
*/
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Specifies the width of the texture in texels.
* @param width texture width in texels, must be at least 1. Default is 1.
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder width(@IntRange(from = 1) int width) {
nBuilderWidth(mNativeBuilder, width);
return this;
}
/**
* Specifies the height of the texture in texels.
* @param height texture height in texels, must be at least 1. Default is 1.
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder height(@IntRange(from = 1) int height) {
nBuilderHeight(mNativeBuilder, height);
return this;
}
/**
* Specifies the texture's number of layers. This creates a 3D texture.
* @param depth texture number of layer, must be at least 1. Default is 1.
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder depth(@IntRange(from = 1) int depth) {
nBuilderDepth(mNativeBuilder, depth);
return this;
}
/**
* Specifies the number of mipmap levels
* @param levels must be at least 1 and less or equal to <code>floor(log<sub>2</sub>(max(width, height))) + 1</code>. Default is 1.
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder levels(@IntRange(from = 1) int levels) {
nBuilderLevels(mNativeBuilder, levels);
return this;
}
/**
* Specifies the type of sampler to use.
* @param target {@link Sampler Sampler} type
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder sampler(@NonNull Sampler target) {
nBuilderSampler(mNativeBuilder, target.ordinal());
return this;
}
/**
* Specifies the texture's internal format.
* <p>The internal format specifies how texels are stored (which may be different from how
* they're specified in {@link #setImage}). {@link InternalFormat InternalFormat} specifies
* both the color components and the data type used.</p>
* @param format texture's {@link InternalFormat internal format}.
* @return This Builder, for chaining calls.
*/
@NonNull
public Builder format(@NonNull InternalFormat format) {
nBuilderFormat(mNativeBuilder, format.ordinal());
@@ -356,6 +626,13 @@ public class Texture {
return this;
}
/**
* Creates a new <code>Texture</code> instance.
* @param engine The {@link Engine} to associate this <code>Texture</code> with.
* @return A newly created <code>Texture</code>
* @exception IllegalStateException if a parameter to a builder function was invalid.
* A mode detailed message about the error is output in the system log.
*/
@NonNull
public Texture build(@NonNull Engine engine) {
long nativeTexture = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -382,38 +659,71 @@ public class Texture {
}
}
/**
* A bitmask to specify how the texture will be used.
*/
public static class Usage {
/** The texture will be used as a color attachment */
public static final int COLOR_ATTACHMENT = 0x1;
/** The texture will be used as a depth attachment */
public static final int DEPTH_ATTACHMENT = 0x2;
/** The texture will be used as a stencil attachment */
public static final int STENCIL_ATTACHMENT = 0x4;
/** The texture content can be set with {@link #setImage} */
public static final int UPLOADABLE = 0x8;
/** The texture can be read from a shader or blitted from */
public static final int SAMPLEABLE = 0x10;
/** by default textures are <code>UPLOADABLE</code> and <code>SAMPLEABLE</code>*/
public static final int DEFAULT = UPLOADABLE | SAMPLEABLE;
}
public static final int BASE_LEVEL = 0;
/**
* Queries the width of a given level of this texture.
* @param level to query the with of. Must be between 0 and {@link #getLevels}
* @return The width in texel of the given level
*/
public int getWidth(@IntRange(from = 0) int level) {
return nGetWidth(getNativeObject(), level);
}
/**
* Queries the height of a given level of this texture.
* @param level to query the height of. Must be between 0 and {@link #getLevels}
* @return The height in texel of the given level
*/
public int getHeight(@IntRange(from = 0) int level) {
return nGetHeight(getNativeObject(), level);
}
/**
* Queries the number of layers of given level of this texture has.
* @param level to query the number of layers of. Must be between 0 and {@link #getLevels}
* @return The number of layers of the given level
*/
public int getDepth(@IntRange(from = 0) int level) {
return nGetDepth(getNativeObject(), level);
}
/**
* @return the number of mipmap levels of this texture
*/
public int getLevels() {
return nGetLevels(getNativeObject());
}
/**
* @return This texture {@link Sampler Sampler} type.
*/
@NonNull
public Sampler getTarget() {
return Sampler.values()[nGetTarget(getNativeObject())];
}
/**
* @return This texture's {@link InternalFormat InternalFormat}.
*/
@NonNull
public InternalFormat getFormat() {
return InternalFormat.values()[nGetInternalFormat(getNativeObject())];
@@ -421,12 +731,65 @@ public class Texture {
// TODO: add a setImage() version that takes an android Bitmap
/**
* <code>setImage</code> is used to modify the whole content of the texure from a CPU-buffer.
*
* <p>This <code>Texture</code> instance must use {@link Sampler#SAMPLER_2D SAMPLER_2D} or
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}. If the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with {@link Sampler#SAMPLER_2D SAMPLER_2D}.</p>
*
* This is equivalent to calling: <br>
*
* <code>setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer)</code>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param level Level to set the image for. Must be less than {@link #getLevels()}.
* @param buffer Client-side buffer containing the image to set.
* <code>buffer</code>'s {@link Format format} must match that
* of {@link #getFormat()}
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*
* @see Builder#sampler
* @see PixelBufferDescriptor
*/
public void setImage(@NonNull Engine engine,
@IntRange(from = 0) int level,
@NonNull PixelBufferDescriptor buffer) {
setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer);
}
/**
* <code>setImage</code> is used to modify a sub-region of the texure from a CPU-buffer.
*
* <p>This <code>Texture</code> instance must use {@link Sampler#SAMPLER_2D SAMPLER_2D} or
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}. If the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with {@link Sampler#SAMPLER_2D SAMPLER_2D}.</p>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param level Level to set the image for. Must be less than {@link #getLevels()}.
* @param xoffset x-offset in texel of the region to modify
* @param yoffset y-offset in texel of the region to modify
* @param width width in texel of the region to modify
* @param height height in texel of the region to modify
* @param buffer Client-side buffer containing the image to set.
* <code>buffer</code>'s {@link Format format} must match that
* of {@link #getFormat()}
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*
* @see Builder#sampler
* @see PixelBufferDescriptor
*/
public void setImage(@NonNull Engine engine,
@IntRange(from = 0) int level,
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@@ -453,9 +816,33 @@ public class Texture {
}
}
// note: faceOffsetsInBytes are offsets in byte in the buffer relative to the current position()
// note: use Texture CubemapFace to index the faceOffsetsInBytes array
// note: we assume all 6 faces are tightly packed
/**
* <code>setImage</code> is used to specify all six images of a cubemap level and
* follows exactly the OpenGL conventions
*
* <p>This <code>Texture</code> instance must use
* {@link Sampler#SAMPLER_CUBEMAP SAMPLER_CUBEMAP}.</p>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param level Level to set the image for. Must be less than {@link #getLevels()}.
* @param buffer Client-side buffer containing the image to set.
* <code>buffer</code>'s {@link Format format} must match that
* of {@link #getFormat()}
* @param faceOffsetsInBytes Offsets in bytes into <code>buffer</code> for all six images.
* The offsets are specified in the following order:
* +x, -x, +y, -y, +z, -z.
*
* <p><code>faceOffsetsInBytes</code> are offsets in byte in the <code>buffer</code> relative
* to the current {@link Buffer#position()}. Use {@link CubemapFace} to index the
* <code>faceOffsetsInBytes</code> array. All six faces must be tightly packed.</p>
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*
* @see Builder#sampler
* @see PixelBufferDescriptor
*/
public void setImage(@NonNull Engine engine, @IntRange(from = 0) int level,
@NonNull PixelBufferDescriptor buffer,
@NonNull @Size(min = 6) int[] faceOffsetsInBytes) {
@@ -478,10 +865,60 @@ public class Texture {
}
}
/**
* Specifies the external image to associate with this <code>Texture</code>.
*
* <p>This <code>Texture</code> instance must use
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}.</p>
* <p>Typically the external image is OS specific, and can be a video or camera frame.
* There are many restrictions when using an external image as a texture, such as:</p>
* <ul>
* <li> only the level of detail (LOD) 0 can be specified</li>
* <li> only {@link TextureSampler.MagFilter#NEAREST NEAREST} or
* {@link TextureSampler.MagFilter#LINEAR LINEAR} filtering is supported</li>
* <li> the size and format of the texture is defined by the external image</li>
* </ul>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param eglImage An opaque handle to a platform specific image. Supported types are
* <code>eglImageOES</code> on Android and <code>CVPixelBufferRef</code> on iOS.
* <p>On iOS the following pixel formats are supported: <ul>
* <li><code>kCVPixelFormatType_32BGRA</code></li>
* <li><code>kCVPixelFormatType_420YpCbCr8BiPlanarFullRange</code></li>
* </ul></p>
*
* @see Builder#sampler
*/
public void setExternalImage(@NonNull Engine engine, long eglImage) {
nSetExternalImage(getNativeObject(), engine.getNativeObject(), eglImage);
}
/**
* Specifies the external stream to associate with this <code>Texture</code>.
*
* <p>This <code>Texture</code> instance must use
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}.</p>
* <p>Typically the external image is OS specific, and can be a video or camera frame.
* There are many restrictions when using an external image as a texture, such as:</p>
* <ul>
* <li> only the level of detail (LOD) 0 can be specified</li>
* <li> only {@link TextureSampler.MagFilter#NEAREST NEAREST} or
* {@link TextureSampler.MagFilter#LINEAR LINEAR} filtering is supported</li>
* <li> the size and format of the texture is defined by the external image</li>
* </ul>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param stream A {@link Stream} object
*
* @exception IllegalStateException if the sampler type is not
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}
*
* @see Stream
* @see Builder#sampler
*
*/
public void setExternalStream(@NonNull Engine engine, @NonNull Stream stream) {
long nativeObject = getNativeObject();
long streamNativeObject = stream.getNativeObject();
@@ -492,10 +929,68 @@ public class Texture {
nSetExternalStream(nativeObject, engine.getNativeObject(), streamNativeObject);
}
/**
* Generates all the mipmap levels automatically. This requires the texture to have a
* color-renderable format.
*
* <p>This <code>Texture</code> instance must <b>not</b> use
* {@link Sampler#SAMPLER_CUBEMAP SAMPLER_CUBEMAP}, or it has no effect.</p>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
*/
public void generateMipmaps(@NonNull Engine engine) {
nGenerateMipmaps(getNativeObject(), engine.getNativeObject());
}
/**
* Creates a reflection map from an environment map.
*
* <p>This is a utility function that replaces calls to {@link #setImage}.
* The provided environment map is processed and all mipmap levels are populated. The
* processing is similar to the offline tool <code>cmgen</code> at a lower quality setting.</p>
*
* <p>This function is intended to be used when the environment cannot be processed offline,
* for instance if it's generated at runtime.</p>
*
* <p>The source data must obey to some constraints:</p>
* <ul>
* <li>the data {@link Format format} must be {@link Format#RGB}</li>
* <li>the data {@link Type type} must be one of
* <ul>
* <li>{@link Type#FLOAT}</li>
* <li>{@link Type#HALF}</li>
* </ul>
* </li>
* </ul>
*
* <p>The current texture must be a cubemap.</p>
*
* <p>The reflections cubemap's {@link InternalFormat internal format} cannot be a compressed format.</p>
*
* <p>The reflections cubemap's dimension must be a power-of-two.</p>
*
* <p>This operation is computationally intensive, especially with large environments and
* is currently <b>synchronous</b>. Expect about 1ms for a 16 &times 16 cubemap.</p>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param buffer Client-side buffer containing the image to set.
* <code>buffer</code>'s {@link Format format} and {@link Type type} must match
* the constraints above.
* @param faceOffsetsInBytes Offsets in bytes into <code>buffer</code> for all six images.
* The offsets are specified in the following order:
* +x, -x, +y, -y, +z, -z.
*
* @param options Optional parameter to control user-specified quality and options.
*
* <p><code>faceOffsetsInBytes</code> are offsets in byte in the <code>buffer</code> relative
* to the current {@link Buffer#position()}. Use {@link CubemapFace} to index the
* <code>faceOffsetsInBytes</code> array. All six faces must be tightly packed.</p>
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*/
public void generatePrefilterMipmap(@NonNull Engine engine,
@NonNull PixelBufferDescriptor buffer, @NonNull @Size(min = 6) int[] faceOffsetsInBytes,
PrefilterOptions options) {

View File

@@ -18,24 +18,60 @@ package com.google.android.filament;
import android.support.annotation.NonNull;
/**
* <code>TextureSampler</code> defines how a texture is accessed.
*/
public class TextureSampler {
public enum WrapMode {
/**
* The edge of the texture extends to infinity.
*/
CLAMP_TO_EDGE,
/**
* The texture infinitely repeats in the wrap direction.
*/
REPEAT,
/**
* The texture infinitely repeats and mirrors in the wrap direction.
*/
MIRRORED_REPEAT
}
public enum MinFilter {
/**
* No filtering. Nearest neighbor is used.
*/
NEAREST,
/**
* Box filtering. Weighted average of 4 neighbors is used.
*/
LINEAR,
/**
* Mip-mapping is activated. But no filtering occurs.
*/
NEAREST_MIPMAP_NEAREST,
/**
* Box filtering within a mip-map level.
*/
LINEAR_MIPMAP_NEAREST,
/**
* Mip-map levels are interpolated, but no other filtering occurs.
*/
NEAREST_MIPMAP_LINEAR,
/**
* Both interpolated Mip-mapping and linear filtering are used.
*/
LINEAR_MIPMAP_LINEAR,
}
public enum MagFilter {
/**
* No filtering. Nearest neighbor is used.
*/
NEAREST,
/**
* Box filtering. Weighted average of 4 neighbors is used.
*/
LINEAR
}
@@ -44,114 +80,246 @@ public class TextureSampler {
COMPARE_TO_TEXTURE
}
/**
* Comparison functions for the depth sampler.
*/
public enum CompareFunction {
/**
* Less or equal
*/
LESS_EQUAL,
/**
* Greater or equal
*/
GREATER_EQUAL,
/**
* Strictly less than
*/
LESS,
/**
* Strictly greater than
*/
GREATER,
/**
* Equal
*/
EQUAL,
/**
* Not equal
*/
NOT_EQUAL,
/**
* Always. Depth testing is deactivated.
*/
ALWAYS,
/**
* Never. The depth test always fails.
*/
NEVER
}
int mSampler = 0; // bit field used by native
/**
* Min filter: LINEAR_MIPMAP_LINEAR
* Mag filter: LINEAR
* Wrap mode: REPEAT
* Initializes the <code>TextureSampler</code> with default values.
* <br>Minification filter: {@link MinFilter#LINEAR_MIPMAP_LINEAR}
* <br>Magnification filter: {@link MagFilter#LINEAR}
* <br>Wrap modes: {@link WrapMode#REPEAT}
*/
public TextureSampler() {
this(MinFilter.LINEAR_MIPMAP_LINEAR, MagFilter.LINEAR, WrapMode.REPEAT);
}
/**
* Initializes the <code>TextureSampler</code> with default values, but specifying the
* minification and magnification filters.
*
* @param minMag {@link MagFilter magnification filter},
* the minification filter will be set to the same value.
*/
public TextureSampler(@NonNull MagFilter minMag) {
this(minMag, WrapMode.CLAMP_TO_EDGE);
}
/**
* Initializes the <code>TextureSampler</code> with user specified values.
*
* @param minMag {@link MagFilter magnification filter},
* the minification filter will be set to the same value.
* @param wrap {@link WrapMode wrapping mode} for all directions
*/
public TextureSampler(@NonNull MagFilter minMag, @NonNull WrapMode wrap) {
this(minFilterFromMagFilter(minMag), minMag, wrap);
}
/**
* Initializes the <code>TextureSampler</code> with user specified values.
*
* @param min {@link MagFilter magnification filter}
* @param mag {@link MinFilter minification filter}
* @param wrap {@link WrapMode wrapping mode} for all directions
*/
public TextureSampler(@NonNull MinFilter min, @NonNull MagFilter mag, @NonNull WrapMode wrap) {
this(min, mag, wrap, wrap, wrap);
}
/**
* Initializes the <code>TextureSampler</code> with user specified values.
*
* @param min {@link MagFilter magnification filter}
* @param mag {@link MinFilter minification filter}
* @param s {@link WrapMode wrapping mode} for the s (horizontal) direction
* @param t {@link WrapMode wrapping mode} for the t (vertical) direction
* @param r {@link WrapMode wrapping mode} fot the r (depth) direction
*/
public TextureSampler(@NonNull MinFilter min, @NonNull MagFilter mag,
@NonNull WrapMode s, @NonNull WrapMode t, @NonNull WrapMode r) {
mSampler = nCreateSampler(min.ordinal(), mag.ordinal(),
s.ordinal(), t.ordinal(), r.ordinal());
}
/**
* Initializes the <code>TextureSampler</code> with user specified comparison mode. The
* comparison fonction is set to {@link CompareFunction#LESS_EQUAL}.
*
* @param mode {@link CompareMode comparison mode}
*/
public TextureSampler(@NonNull CompareMode mode) {
this(mode, CompareFunction.LESS_EQUAL);
}
/**
* Initializes the <code>TextureSampler</code> with user specified comparison mode and function.
*
* @param mode {@link CompareMode comparison mode}
* @param function {@link CompareFunction comparison function}
*/
public TextureSampler(@NonNull CompareMode mode, @NonNull CompareFunction function) {
mSampler = nCreateCompareSampler(mode.ordinal(), function.ordinal());
}
/**
* @return the minification filter
*/
public MinFilter getMinFilter() {
return MinFilter.values()[nGetMinFilter(mSampler)];
}
/**
* Sets the minification filter.
*
* @param filter minification filter
*/
public void setMinFilter(MinFilter filter) {
mSampler = nSetMinFilter(mSampler, filter.ordinal());
}
/**
* @return the magnification filter
*/
public MagFilter getMagFilter() {
return MagFilter.values()[nGetMagFilter(mSampler)];
}
/**
* Sets the magnification filter.
*
* @param filter magnification filter
*/
public void setMagFilter(MagFilter filter) {
mSampler = nSetMagFilter(mSampler, filter.ordinal());
}
/**
* @return the wrapping mode in the s (horizontal) direction
*/
public WrapMode getWrapModeS() {
return WrapMode.values()[nGetWrapModeS(mSampler)];
}
/**
* Sets the wrapping mode in the s (horizontal) direction.
* @param mode wrapping mode
*/
public void setWrapModeS(WrapMode mode) {
mSampler = nSetWrapModeS(mSampler, mode.ordinal());
}
/**
* @return the wrapping mode in the t (vertical) direction
*/
public WrapMode getWrapModeT() {
return WrapMode.values()[nGetWrapModeT(mSampler)];
}
/**
* Sets the wrapping mode in the t (vertical) direction.
* @param mode wrapping mode
*/
public void setWrapModeT(WrapMode mode) {
mSampler = nSetWrapModeT(mSampler, mode.ordinal());
}
/**
* @return the wrapping mode in the r (depth) direction
*/
public WrapMode getWrapModeR() {
return WrapMode.values()[nGetWrapModeR(mSampler)];
}
/**
* Sets the wrapping mode in the t (depth) direction.
* @param mode wrapping mode
*/
public void setWrapModeR(WrapMode mode) {
mSampler = nSetWrapModeR(mSampler, mode.ordinal());
}
/**
* @return the anisotropy value
* @see #setAnisotropy
*/
public float getAnisotropy() {
return nGetAnisotropy(mSampler);
}
/**
* This controls anisotropic filtering.
*
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0.
* The maximum permissible value is 7.
*/
public void setAnisotropy(float anisotropy) {
mSampler = nSetAnisotropy(mSampler, anisotropy);
}
/**
* @return the comparison mode
*/
public CompareMode getCompareMode() {
return CompareMode.values()[nGetCompareMode(mSampler)];
}
/**
* Sets the comparison mode.
*
* @param mode comparison mode
*/
public void setCompareMode(CompareMode mode) {
mSampler = nSetCompareMode(mSampler, mode.ordinal());
}
/**
* @return the comparison function
*/
public CompareFunction getCompareFunction() {
return CompareFunction.values()[nGetCompareFunction(mSampler)];
}
/**
* Sets the comparison function.
* @param function the comparison function
*/
public void setCompareFunction(CompareFunction function) {
mSampler = nSetCompareFunction(mSampler, function.ordinal());
}

View File

@@ -20,47 +20,154 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.Size;
public class TransformManager {
/**
* <code>TransformManager</code> is used to add transform components to entities.
*
* <p>A transform component gives an entity a position and orientation in space in the coordinate
* space of its parent transform. The <code>TransformManager</code> takes care of computing the
* world-space transform of each component (i.e. its transform relative to the root).</p>
*
* <h1>Creation and destruction</h1>
*
* A transform component is created using {@link TransformManager#create} and destroyed by calling
* {@link TransformManager#destroy}.
*
* <pre>
* Engine engine = Engine.create();
* EntityManager entityManager = EntityManager().get();
* int object = entityManager.create();
*
* TransformManager tcm = engine.getTransformManager();
*
* // create the transform component
* tcm.create(object);
*
* // set its transform
* float[] transform = ...; // transform to set
* EntityInstance i = tcm.getInstance(object);
* tcm.setTransform(i, transform));
*
* // destroy the transform component
* tcm.destroy(object);
* </pre>
*
*/public class TransformManager {
private long mNativeObject;
TransformManager(long nativeTransformManager) {
mNativeObject = nativeTransformManager;
}
/**
* Returns whether a particular {@link Entity} is associated with a component of this
* <code>TransformManager</code>
*
* @param entity an {@link Entity}
* @return true if this {@link Entity} has a component associated with this manager
*/
public boolean hasComponent(@Entity int entity) {
return nHasComponent(mNativeObject, entity);
}
/**
* Gets an {@link EntityInstance} representing the transform component associated with the
* given {@link Entity}.
*
* @param entity an {@link Entity}
* @return an {@link EntityInstance}, which represents the transform component associated with
* the {@link Entity} <code>entity</code>
* @see #hasComponent
*/
@EntityInstance
public int getInstance(@Entity int entity) {
return nGetInstance(mNativeObject, entity);
}
/**
* Creates a transform component and associates it with the given entity. The component is
* initialized with the identity transform.
* If this component already exists on the given entity, it is first
* destroyed as if {@link #destroy} was called.
*
* @param entity an {@link Entity} to associate a transform component to.
* @see #destroy
*/
@EntityInstance
public int create(@Entity int entity) {
return nCreate(mNativeObject, entity);
}
/**
* Creates a transform component with a parent and associates it with the given entity.
* If this component already exists on the given entity, it is first
* destroyed as if {@link #destroy} was called.
*
* @param entity an {@link Entity} to associate a transform component to.
* @param parent the {@link EntityInstance} of the parent transform
* @param localTransform the transform, relative to the parent, to initialize the transform
* component with.
* @see #destroy
*/
@EntityInstance
public int create(@Entity int entity, @EntityInstance int parent,
@Nullable @Size(min = 16) float[] localTransform) {
return nCreateArray(mNativeObject, entity, parent, localTransform);
}
/**
* Destroys this component from the given entity, children are orphaned.
*
* @param entity an {@link Entity}.
* If this transform had children, these are orphaned, which means their local
* transform becomes a world transform. Usually it's nonsensical.
* It's recommended to make sure that a destroyed transform doesn't have children.
* @see #create
*/
public void destroy(@Entity int entity) {
nDestroy(mNativeObject, entity);
}
/**
* Re-parents an entity to a new one.
*
* @param i the {@link EntityInstance} of the transform component to re-parent
* @param newParent the {@link EntityInstance} of the new parent transform.
* It is an error to re-parent an entity to a descendant and will cause
* undefined behaviour.
* @see #getInstance
*/
public void setParent(@EntityInstance int i, @EntityInstance int newParent) {
nSetParent(mNativeObject, i, newParent);
}
/**
* Sets a local transform of a transform component.
* <p>This operation can be slow if the hierarchy of transform is too deep, and this
* will be particularly bad when updating a lot of transforms. In that case,
* consider using {@link #openLocalTransformTransaction} / {@link #commitLocalTransformTransaction}.</p>
*
* @param i the {@link EntityInstance} of the transform component to set the local
* transform to.
* @param localTransform the local transform (i.e. relative to the parent).
* @see #getTransform
*/
public void setTransform(@EntityInstance int i,
@NonNull @Size(min = 16) float[] localTransform) {
Asserts.assertMat4fIn(localTransform);
nSetTransform(mNativeObject, i, localTransform);
}
/**
* Returns the local transform of a transform component.
*
* @param i the {@link EntityInstance} of the transform component to query the
* local transform from.
* @param outLocalTransform a 16 <code>float</code> array to receive the result.
* If <code>null</code> is given, a new suitable array is allocated.
* @return the local transform of the component (i.e. relative to the parent). This always
* returns the value set by setTransform().
* @see #setTransform
*/
@NonNull
@Size(min = 16)
public float[] getTransform(@EntityInstance int i,
@@ -70,6 +177,17 @@ public class TransformManager {
return outLocalTransform;
}
/**
* Returns the world transform of a transform component.
*
* @param i the {@link EntityInstance} of the transform component to query the
* world transform from.
* @param outWorldTransform a 16 <code>float</code> array to receive the result.
* If <code>null</code> is given, a new suitable array is allocated
* @return The world transform of the component (i.e. relative to the root). This is the
* composition of this component's local transform with its parent's world transform.
* @see #setTransform
*/
@NonNull
@Size(min = 16)
public float[] getWorldTransform(@EntityInstance int i,
@@ -79,10 +197,36 @@ public class TransformManager {
return outWorldTransform;
}
/**
* Opens a local transform transaction. During a transaction, {@link #getWorldTransform} can
* return an invalid transform until {@link #commitLocalTransformTransaction} is called.
* However, {@link #setTransform} will perform significantly better and in constant time.
*
* <p>This is useful when updating many transforms and the transform hierarchy is deep (say more
* than 4 or 5 levels).</p>
*
* <p>If the local transform transaction is already open, this is a no-op.</p>
*
* @see #commitLocalTransformTransaction
* @see #setTransform
*/
public void openLocalTransformTransaction() {
nOpenLocalTransformTransaction(mNativeObject);
}
/**
* Commits the currently open local transform transaction. When this returns, calls
* to {@link #getWorldTransform} will return the proper value.
*
* <p>Failing to call this method when done updating the local transform will cause
* a lot of rendering problems. The system never closes the transaction
* automatically.</p>
*
* <p>If the local transform transaction is not open, this is a no-op.</p>
*
* @see #openLocalTransformTransaction
* @see #setTransform
*/
public void commitLocalTransformTransaction() {
nCommitLocalTransformTransaction(mNativeObject);
}

View File

@@ -23,6 +23,33 @@ import android.support.annotation.Nullable;
import java.nio.Buffer;
import java.nio.BufferOverflowException;
/**
* Holds a set of buffers that define the geometry of a <code>Renderable</code>.
*
* <p>
* The geometry of the <code>Renderable</code> itself is defined by a set of vertex attributes such as
* position, color, normals, tangents, etc...
* </p>
*
* <p>
* There is no need to have a 1-to-1 mapping between attributes and buffer. A buffer can hold the
* data of several attributes -- attributes are then referred as being "interleaved".
* </p>
*
* <p>
* The buffers themselves are GPU resources, therefore mutating their data can be relatively slow.
* For this reason, it is best to separate the constant data from the dynamic data into multiple
* buffers.
* </p>
*
* <p>
* It is possible, and even encouraged, to use a single vertex buffer for several
* <code>Renderable</code>s.
* </p>
*
* @see IndexBuffer
* @see RenderableManager
*/
public class VertexBuffer {
private long mNativeObject;
@@ -77,22 +104,47 @@ public class VertexBuffer {
HALF3,
HALF4,
}
/**
* Specifies the quaternion type for the {@link #populateTangentQuaternions} utility.
*/
public enum QuatType {
HALF4, // 2 bytes per component as half-floats (8 bytes per quat)
SHORT4, // 2 bytes per component as normalized integers (8 bytes per quat)
FLOAT4, // 4 bytes per component as floats (16 bytes per quat)
/** 2 bytes per component as half-floats (8 bytes per quat) */
HALF4,
/** 2 bytes per component as normalized integers (8 bytes per quat) */
SHORT4,
/** 4 bytes per component as floats (16 bytes per quat) */
FLOAT4,
}
/**
* Specifies the parameters for the {@link #populateTangentQuaternions} utility.
*/
public static class QuatTangentContext {
public QuatType quatType; // desired quaternion type (required)
public int quatCount; // number of quaternions (required)
public Buffer outBuffer; // pre-allocated output buffer (required)
public int outStride; // desired stride in bytes (optional)
public Buffer normals; // source normals (required)
public int normalsStride; // normals stride in bytes (optional)
public Buffer tangents; // source tangents (optional)
public int tangentsStride; // tangents stride in bytes (optional)
/** desired quaternion type (required) */
public QuatType quatType;
/** number of quaternions (required) */
public int quatCount;
/** pre-allocated output buffer (required) */
public Buffer outBuffer;
/** desired stride in bytes (optional) */
public int outStride;
/** source normals (required) */
public Buffer normals;
/** normals stride in bytes (optional) */
public int normalsStride;
/** source tangents (optional) */
public Buffer tangents;
/** tangents stride in bytes (optional) */
public int tangentsStride;
}
public static class Builder {
@@ -105,18 +157,64 @@ public class VertexBuffer {
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Size of each buffer in this set, expressed in in number of vertices.
*
* @param vertexCount number of vertices in each buffer in this set
*
* @return A reference to this Builder for chaining calls.
*/
@NonNull
public Builder vertexCount(@IntRange(from = 1) int vertexCount) {
nBuilderVertexCount(mNativeBuilder, vertexCount);
return this;
}
/**
* Defines how many buffers will be created in this vertex buffer set. These buffers are
* later referenced by index from 0 to <code>bufferCount</code> - 1.
*
* This call is mandatory. The default is 0.
*
* @param bufferCount number of buffers in this vertex buffer set. The maximum value is 8.
*
* @return this <code>Builder</code> for chaining calls
*/
@NonNull
public Builder bufferCount(@IntRange(from = 1) int bufferCount) {
nBuilderBufferCount(mNativeBuilder, bufferCount);
return this;
}
/**
* Sets up an attribute for this vertex buffer set.
*
* Using <code>byteOffset</code> and <code>byteStride</code>, attributes can be interleaved
* in the same buffer.
*
* <p>
* This is a no-op if the <code>attribute</code> is an invalid enum.
* This is a no-op if the <code>bufferIndex</code> is out of bounds.
* </p>
*
* <p>
* Warning: <code>VertexAttribute.TANGENTS</code> must be specified as a quaternion and is
* how normals are specified.
* </p>
*
* @param attribute the attribute to set up
* @param bufferIndex the index of the buffer containing the data for this attribute. Must
* be between 0 and bufferCount() - 1.
* @param attributeType the type of the attribute data (e.g. byte, float3, etc...)
* @param byteOffset offset in <i>bytes</i> into the buffer <code>bufferIndex</code>
* @param byteStride stride in <i>bytes</i> to the next element of this attribute. When
* set to zero the attribute size, as defined by
* <code>attributeType</code> is used.
*
* @return A reference to this <code>Builder</code> for chaining calls.
*
* @see VertexAttribute
*/
@NonNull
public Builder attribute(@NonNull VertexAttribute attribute,
@IntRange(from = 0) int bufferIndex, @NonNull AttributeType attributeType,
@@ -126,24 +224,82 @@ public class VertexBuffer {
return this;
}
/**
* Sets up an attribute for this vertex buffer set.
*
* Using <code>byteOffset</code> and <code>byteStride</code>, attributes can be interleaved
* in the same buffer.
*
* <p>
* This is a no-op if the <code>attribute</code> is an invalid enum.
* This is a no-op if the <code>bufferIndex</code> is out of bounds.
* </p>
*
* <p>
* Warning: <code>VertexAttribute.TANGENTS</code> must be specified as a quaternion and is
* how normals are specified.
* </p>
*
* @param attribute the attribute to set up
* @param bufferIndex the index of the buffer containing the data for this attribute. Must
* be between 0 and bufferCount() - 1.
* @param attributeType the type of the attribute data (e.g. byte, float3, etc...)
*
* @return A reference to this <code>Builder</code> for chaining calls.
*
* @see VertexAttribute
*/
@NonNull
public Builder attribute(@NonNull VertexAttribute attribute,
@IntRange(from = 0) int bufferIndex, @NonNull AttributeType attributeType) {
return attribute(attribute, bufferIndex, attributeType, 0, 0 );
}
/**
* Sets whether a given attribute should be normalized. By default attributes are not
* normalized. A normalized attribute is mapped between 0 and 1 in the shader. This applies
* only to integer types.
*
* @param attribute enum of the attribute to set the normalization flag to
*
* @return this <code>Builder</code> object for chaining calls.
*
* This is a no-op if the <code>attribute</code> is an invalid enum.
*/
@NonNull
public Builder normalized(@NonNull VertexAttribute attribute) {
nBuilderNormalized(mNativeBuilder, attribute.ordinal(), true);
return this;
}
/**
* Sets whether a given attribute should be normalized. By default attributes are not
* normalized. A normalized attribute is mapped between 0 and 1 in the shader. This applies
* only to integer types.
*
* @param attribute enum of the attribute to set the normalization flag to
* @param enabled true to automatically normalize the given attribute
*
* @return this <code>Builder</code> object for chaining calls.
*
* This is a no-op if the <code>attribute</code> is an invalid enum.
*/
@NonNull
public Builder normalized(@NonNull VertexAttribute attribute, boolean enabled) {
nBuilderNormalized(mNativeBuilder, attribute.ordinal(), enabled);
return this;
}
/**
* Creates the <code>VertexBuffer</code> object and returns a pointer to it.
*
* @param engine reference to the {@link Engine} to associate this <code>VertexBuffer</code>
* with
*
* @return the newly created <code>VertexBuffer</code> object
*
* @exception IllegalStateException if the VertexBuffer could not be created
*/
@NonNull
public VertexBuffer build(@NonNull Engine engine) {
long nativeVertexBuffer = nBuilderBuild(mNativeBuilder, engine.getNativeObject());
@@ -168,24 +324,68 @@ public class VertexBuffer {
}
}
/**
* Returns the vertex count.
*
* @return number of vertices in this vertex buffer set
*/
@IntRange(from = 0)
public int getVertexCount() {
return nGetVertexCount(getNativeObject());
}
/**
* Asynchronously copy-initializes the specified buffer from the given buffer data.
*
* @param engine reference to the {@link Engine} to associate this
* <code>VertexBuffer</code> with
* @param bufferIndex index of the buffer to initialize. Must be between 0 and
* bufferCount() - 1.
* @param buffer a CPU-side {@link Buffer} representing the data used to initialize
* the <code>VertexBuffer</code> at index <code>bufferIndex</code>.
* <code>buffer</code> should contain raw, untyped data that will
* be copied as-is into the buffer.
*/
public void setBufferAt(@NonNull Engine engine, int bufferIndex, @NonNull Buffer buffer) {
setBufferAt(engine, bufferIndex, buffer, 0, 0, null, null);
}
/**
* Asynchronously copy-initializes a region of the specified buffer from the given buffer data.
*
* @param engine reference to the {@link Engine} to associate this
* <code>VertexBuffer</code> with
* @param bufferIndex index of the buffer to initialize. Must be between 0 and
* bufferCount() - 1.
* @param buffer a CPU-side {@link Buffer} representing the data used to initialize
* the <code>VertexBuffer</code> at index <code>bufferIndex</code>.
* <code>buffer</code> should contain raw, untyped data that will
* be copied as-is into the buffer.
* @param destOffsetInBytes offset in <i>bytes</i> into the buffer at index
* <code>bufferIndex</code> of this vertex buffer set.
*/
public void setBufferAt(@NonNull Engine engine, int bufferIndex, @NonNull Buffer buffer,
@IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count) {
setBufferAt(engine, bufferIndex, buffer, destOffsetInBytes, count, null, null);
}
/**
* Valid handler types:
* - Android: Handler, Executor
* - Other: Executor
* Asynchronously copy-initializes a region of the specified buffer from the given buffer data.
*
* @param engine reference to the {@link Engine} to associate this
* <code>VertexBuffer</code> with
* @param bufferIndex index of the buffer to initialize. Must be between 0 and
* bufferCount() - 1.
* @param buffer a CPU-side {@link Buffer} representing the data used to initialize
* the <code>VertexBuffer</code> at index <code>bufferIndex</code>.
* <code>buffer</code> should contain raw, untyped data that will
* be copied as-is into the buffer.
* @param destOffsetInBytes offset in <i>bytes</i> into the buffer at index
* <code>bufferIndex</code> of this vertex buffer set.
* @param handler an {@link java.util.concurrent.Executor Executor}. On Android this
* can also be a {@link android.os.Handler Handler}.
* @param callback a callback executed by <code>handler</code> when <code>buffer</code>
* is no longer needed.
*/
public void setBufferAt(@NonNull Engine engine, int bufferIndex, @NonNull Buffer buffer,
@IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count,
@@ -197,7 +397,27 @@ public class VertexBuffer {
throw new BufferOverflowException();
}
}
/**
* Convenience function that consumes normal vectors (and, optionally, tangent vectors) and
* produces quaternions that can be passed into a TANGENTS buffer.
*
* <p>
* The given output buffer must be preallocated with at least quatCount * outStride bytes.
* <p>
*
* <p>
* Normals are required but tangents are optional, in which case this function tries to generate
* reasonable tangents. The given normals should be unit length.
* </p>
*
* <p>
* If supplied, the tangent vectors should be unit length and should be orthogonal to the
* normals. The w component of the tangent is a sign (-1 or +1) indicating handedness of the
* basis.
* </p>
*
* @param context an initialized QuatTangentContext object
*/
public static void populateTangentQuaternions(@NonNull QuatTangentContext context) {
nPopulateTangentQuaternions(context.quatType.ordinal(), context.quatCount,
context.outBuffer, context.outBuffer.remaining(), context.outStride,

View File

@@ -23,6 +23,37 @@ import android.support.annotation.Size;
import static com.google.android.filament.Colors.LinearColor;
/**
* Encompasses all the state needed for rendering a {@link Scene}.
*
* <p>
* {@link Renderer#render} operates on <code>View</code> objects. These <code>View</code> objects
* specify important parameters such as:
* </p>
*
* <ul>
* <li>The Scene</li>
* <li>The Camera</li>
* <li>The Viewport</li>
* <li>Some rendering parameters</li>
* </ul>
*
* <p>
* <code>View</code> instances are heavy objects that internally cache a lot of data needed for
* rendering. It is not advised for an application to use many View objects.
* </p>
*
* <p>
* For example, in a game, a <code>View</code> could be used for the main scene and another one for
* the game's user interface. More <code>View</code> instances could be used for creating special
* effects (e.g. a <code>View</code> is akin to a rendering pass).
* </p>
*
* @see Renderer
* @see Scene
* @see Camera
* @see RenderTarget
*/
public class View {
private long mNativeObject;
private String mName;
@@ -35,24 +66,102 @@ public class View {
private AmbientOcclusionOptions mAmbientOcclusionOptions;
private RenderTarget mRenderTarget;
/**
* Dynamic resolution can be used to either reach a desired target frame rate by lowering the
* resolution of a <code>View</code>, or to increase the quality when the rendering is faster
* than the target frame rate.
*
* <p>
* This structure can be used to specify the minimum scale factor used when lowering the
* resolution of a <code>View</code>, and the maximum scale factor used when increasing the
* resolution for higher quality rendering. The scale factors can be controlled on each X and Y
* axis independently. By default, all scale factors are set to 1.0.
* </p>
*
* <p>
* Dynamic resolution is only supported on platforms where the time to render a frame can be
* measured accurately. Dynamic resolution is currently only supported on Android.
* </p>
*/
public static class DynamicResolutionOptions {
/**
* Enables or disables dynamic resolution on a View.
*/
public boolean enabled = false;
/**
* If false, the system scales the major axis first.
*/
public boolean homogeneousScaling = false;
/**
* Desired frame time in milliseconds.
*/
public float targetFrameTimeMilli = 1000.0f / 60.0f;
/**
* Additional headroom for the GPU as a ratio of the targetFrameTime.
*/
public float headRoomRatio = 0.0f;
/**
* Rate at which the scale will change to reach the target frame rate.
*/
public float scaleRate = 0.125f;
/**
* The minimum scale in X and Y this View should use.
*/
public float minScale = 0.5f;
/**
* The maximum scale in X and Y this View should use.
*/
public float maxScale = 1.0f;
/**
* History size. higher values, tend to filter more (clamped to 30).
*/
public int history = 9;
}
/**
* Options for Ambient Occlusion
* @see #setAmbientOcclusion
*/
public static class AmbientOcclusionOptions {
/**
* Ambient Occlusion radius in meters, between 0 and ~10.
*/
public float radius = 0.3f;
/**
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
*/
public float bias = 0.005f;
/**
* Controls ambient occlusion's contrast. Between 0 (linear) and 1 (squared)
*/
public float power = 0.0f;
/**
* How each dimension of the AO buffer is scaled. Must be positive and <= 1.
*/
public float resolution = 0.5f;
}
/**
* Sets the quality of the HDR color buffer.
*
* <p>
* A quality of <code>HIGH</code> or <code>ULTRA</code> means using an RGB16F or RGBA16F color
* buffer. This means colors in the LDR range (0..1) have 10 bit precision. A quality of
* <code>LOW</code> or <code>MEDIUM</code> means using an R11G11B10F opaque color buffer or an
* RGBA16F transparent color buffer. With R11G11B10F colors in the LDR range have a precision of
* either 6 bits (red and green channels) or 5 bits (blue channel).
* </p>
*/
public enum QualityLevel {
LOW,
MEDIUM,
@@ -60,30 +169,73 @@ public class View {
ULTRA
}
/**
* Structure used to set the color precision for the rendering of a <code>View</code>.
*
* <p>
* This structure offers separate quality settings for different parts of the rendering
* pipeline.
* </p>
*
* @see #setRenderQuality
* @see #getRenderQuality
*/
public static class RenderQuality {
public QualityLevel hdrColorBuffer = QualityLevel.HIGH;
}
/**
* List of available ambient occlusion techniques.
*
* @see #setAmbientOcclusion
*/
public enum AmbientOcclusion {
NONE,
SSAO
}
/**
* List of available post-processing anti-aliasing techniques.
*
* @see #setAntiAliasing
* @see #getAntiAliasing
*/
public enum AntiAliasing {
/**
* No anti aliasing performed as part of post-processing.
*/
NONE,
/**
* FXAA is a low-quality but very efficient type of anti-aliasing. (default).
*/
FXAA
}
/**
* List of available tone-mapping operators
*/
public enum ToneMapping {
/**
* Equivalent to disabling tone-mapping.
*/
LINEAR,
/**
* The Academy Color Encoding System (ACES).
*/
ACES
}
/**
* List of available post-processing dithering techniques.
*/
public enum Dithering {
NONE,
TEMPORAL
}
/** @see #setDepthPrepass */
public enum DepthPrepass {
DEFAULT(-1),
DISABLED(0),
@@ -100,52 +252,135 @@ public class View {
mNativeObject = nativeView;
}
/**
* Sets the View's name. Only useful for debugging.
*/
public void setName(@NonNull String name) {
mName = name;
nSetName(getNativeObject(), name);
}
/**
* Returns the View's name.
*/
@Nullable
public String getName() {
return mName;
}
/**
* Sets this View instance's Scene.
*
* <p>
* This method associates the specified Scene with this View. Note that a particular scene can
* be associated with several View instances. To remove an existing association, simply pass
* null.
* </p>
*
* <p>
* The View does not take ownership of the Scene pointer. Before destroying a Scene, be sure
* to remove it from all assoicated Views.
* </p>
*
* @see #getScene
*/
public void setScene(@Nullable Scene scene) {
mScene = scene;
nSetScene(getNativeObject(), scene == null ? 0 : scene.getNativeObject());
}
/**
* Gets this View's associated Scene, or null if none has been assigned.
*
* @see #setScene
*/
@Nullable
public Scene getScene() {
return mScene;
}
/**
* Sets this View's Camera.
*
* <p>
* This method associates the specified Camera with this View. A Camera can be associated with
* several View instances. To remove an existing association, simply pass
* null.
* </p>
*
* <p>
* The View does not take ownership of the Scene pointer. Before destroying a Camera, be sure
* to remove it from all assoicated Views.
* </p>
*
* @see #getCamera
*/
public void setCamera(@Nullable Camera camera) {
mCamera = camera;
nSetCamera(getNativeObject(), camera == null ? 0 : camera.getNativeObject());
}
/**
* Gets this View's associated Camera, or null if none has been assigned.
*
* @see #setCamera
*/
@Nullable
public Camera getCamera() {
return mCamera;
}
/**
* Specifies the rectangular rendering area.
*
* <p>
* The viewport specifies where the content of the View (i.e. the Scene) is rendered in
* the render target. The render target is automatically clipped to the Viewport.
* </p>
*
* <p>
* If you wish subsequent changes to take effect please call this method again in order to
* propagate the changes down to the native layer.
* </p>
*
* @param viewport The Viewport to render the Scene into.
*/
public void setViewport(@NonNull Viewport viewport) {
mViewport = viewport;
nSetViewport(getNativeObject(),
mViewport.left, mViewport.bottom, mViewport.width, mViewport.height);
}
/**
* Returns the rectangular rendering area.
*
* @see #setViewport
*/
@NonNull
public Viewport getViewport() {
return mViewport;
}
/**
* Sets the color used to clear the Viewport when rendering this View.
*
* <p>This is ignored if a {@link Skybox} is present or if clearing has been disabled
* via {@link #setClearTargets}. Defaults to black.</p>
*
* @see #getClearColor
*/
public void setClearColor(
@LinearColor float r, @LinearColor float g, @LinearColor float b, float a) {
nSetClearColor(getNativeObject(), r, g, b, a);
}
/**
* Returns the View clear color in a provided 4-tuple.
*
* @return A reference to the passed-in array.
*
* @see #setClearColor
*/
@NonNull @Size(min = 4)
public float[] getClearColor(@NonNull @Size(min = 4) float[] out) {
out = Asserts.assertFloat4(out);
@@ -153,65 +388,178 @@ public class View {
return out;
}
/**
* Sets which targets to clear (default: true, true, false)
*
* @see #setClearColor
*/
public void setClearTargets(boolean color, boolean depth, boolean stencil) {
nSetClearTargets(getNativeObject(), color, depth, stencil);
}
/**
* Sets which layers are visible.
*
* <p>
* Renderable objects can have one or several layers associated to them. Layers are
* represented with an 8-bits bitmask, where each bit corresponds to a layer.
* By default all layers are visible.
* </p>
*
* @see RenderableManager#setLayerMask
*
* @param select a bitmask specifying which layer to set or clear using <code>values</code>.
* @param values a bitmask where each bit sets the visibility of the corresponding layer
* (1: visible, 0: invisible), only layers in <code>select</code> are affected.
*/
public void setVisibleLayers(
@IntRange(from = 0, to = 255) int select,
@IntRange(from = 0, to = 255) int values) {
nSetVisibleLayers(getNativeObject(), select & 0xFF, values & 0xFF);
}
/**
* Enables or disables shadow mapping. Enabled by default.
*
* @see LightManager.Builder#castShadows
* @see RenderableManager.Builder#receiveShadows
* @see RenderableManager.Builder#castShadows
*/
public void setShadowsEnabled(boolean enabled) {
nSetShadowsEnabled(getNativeObject(), enabled);
}
/**
* Specifies an offscreen render target to render into.
*
* <p>
* By default, the view's associated render target is null, which corresponds to the
* SwapChain associated with the engine.
* </p>
*
* @param target render target associated with view, or null for the swap chain
*/
public void setRenderTarget(@Nullable RenderTarget target) {
mRenderTarget = target;
nSetRenderTarget(getNativeObject(), target != null ? target.getNativeObject() : 0);
}
/**
* Gets the offscreen render target associated with this view.
*
* Returns null if the render target is the swap chain (which is default).
*
* @see #setRenderTarget
*/
@Nullable
public RenderTarget getRenderTarget() {
return mRenderTarget;
}
/**
* Sets how many samples are to be used for MSAA in the post-process stage.
* Default is 1 and disables MSAA.
*
* <p>
* Note that anti-aliasing can also be performed in the post-processing stage, generally at
* lower cost. See the FXAA option in {@link #setAntiAliasing}.
* </p>
*
* @param count number of samples to use for multi-sampled anti-aliasing.
*/
public void setSampleCount(int count) {
nSetSampleCount(getNativeObject(), count);
}
/**
* Returns the effective MSAA sample count.
*
* <p>
* A value of 0 or 1 means MSAA is disabled.
* </p>
*
* @return value set by {@link #setSampleCount}
*/
public int getSampleCount() {
return nGetSampleCount(getNativeObject());
}
/**
* Enables or disables anti-aliasing in the post-processing stage. Enabled by default.
*
* <p>
* For MSAA anti-aliasing, see {@link #setSampleCount}.
* </p>
*
* @param type FXAA for enabling, NONE for disabling anti-aliasing.
*/
public void setAntiAliasing(@NonNull AntiAliasing type) {
nSetAntiAliasing(getNativeObject(), type.ordinal());
}
/**
* Queries whether anti-aliasing is enabled during the post-processing stage. To query
* whether MSAA is enabled, see {@link #getSampleCount}.
*
* @return The post-processing anti-aliasing method.
*/
@NonNull
public AntiAliasing getAntiAliasing() {
return AntiAliasing.values()[nGetAntiAliasing(getNativeObject())];
}
/**
* Enables or disables tone-mapping in the post-processing stage. Enabled by default.
*
* @param type Tone-mapping function.
*/
public void setToneMapping(@NonNull ToneMapping type) {
nSetToneMapping(getNativeObject(), type.ordinal());
}
/**
* Returns the tone-mapping function.
* @return tone-mapping function.
*/
@NonNull
public ToneMapping getToneMapping() {
return ToneMapping.values()[nGetToneMapping(getNativeObject())];
}
/**
* Enables or disables dithering in the post-processing stage. Enabled by default.
*
* @param dithering dithering type
*/
public void setDithering(@NonNull Dithering dithering) {
nSetDithering(getNativeObject(), dithering.ordinal());
}
/**
* Queries whether dithering is enabled during the post-processing stage.
*
* @return the current dithering type for this view.
*/
@NonNull
public Dithering getDithering() {
return Dithering.values()[nGetDithering(getNativeObject())];
}
/**
* Sets the dynamic resolution options for this view.
*
* <p>
* Dynamic resolution options controls whether dynamic resolution is enabled, and if it is,
* how it behaves.
* </p>
*
* <p>
* If you wish subsequent changes to take effect please call this method again in order to
* propagate the changes down to the native layer.
* </p>
*
* @param options The dynamic resolution options to use on this view
*/
public void setDynamicResolutionOptions(@NonNull DynamicResolutionOptions options) {
mDynamicResolution = options;
nSetDynamicResolutionOptions(getNativeObject(),
@@ -225,6 +573,10 @@ public class View {
options.history);
}
/**
* Returns the dynamic resolution options associated with this view.
* @return value set by {@link #setDynamicResolutionOptions}.
*/
@NonNull
public DynamicResolutionOptions getDynamicResolutionOptions() {
if (mDynamicResolution == null) {
@@ -233,11 +585,20 @@ public class View {
return mDynamicResolution;
}
/**
* Sets the rendering quality for this view (e.g. color precision).
*
* @param renderQuality The render quality to use on this view
*/
public void setRenderQuality(@NonNull RenderQuality renderQuality) {
mRenderQuality = renderQuality;
nSetRenderQuality(getNativeObject(), renderQuality.hdrColorBuffer.ordinal());
}
/**
* Returns the render quality used by this view.
* @return value set by {@link #setRenderQuality}.
*/
@NonNull
public RenderQuality getRenderQuality() {
if (mRenderQuality == null) {
@@ -246,50 +607,168 @@ public class View {
return mRenderQuality;
}
/**
* Checks if this view is rendered with a depth-only prepass.
*
* @return the value set by {@link #setDepthPrepass}.
*/
@NonNull
public DepthPrepass getDepthPrepass() {
return mDepthPrepass;
}
/**
* Sets whether this view is rendered with or without a depth pre-pass.
*
* <p>
* By default, the system picks the most appropriate strategy for your platform; this method
* lets you override that strategy.
* </p>
*
* <p>
* When the depth pre-pass is enabled, the renderer will first draw all objects in the
* depth buffer from front to back, and then draw the objects again but sorted to minimize
* state changes. With the depth pre-pass disabled, objects are drawn only once, but it may
* result in more state changes or more overdraw.
* </p>
*
* <p>
* The best strategy may depend on the scene and/or GPU.
* </p>
*
* <ul>
* <li>DepthPrepass::DEFAULT uses the most appropriate strategy</li>
* <li>DepthPrepass::DISABLED disables the depth pre-pass</li>
* <li>DepthPrepass::ENABLE enables the depth pre-pass</li>
* </ul>
*/
public void setDepthPrepass(@NonNull DepthPrepass depthPrepass) {
mDepthPrepass = depthPrepass;
nSetDepthPrepass(getNativeObject(), depthPrepass.value);
}
/**
* Returns true if post-processing is enabled.
*
* @see #setPostProcessingEnabled
*/
public boolean isPostProcessingEnabled() {
return nIsPostProcessingEnabled(getNativeObject());
}
/**
* Enables or disables post processing. Enabled by default.
*
* <p>Post-processing includes:</p>
* <ul>
* <li>Tone-mapping & gamma encoding</li>
* <li>Dithering</li>
* <li>MSAA</li>
* <li>FXAA</li>
* <li>Dynamic scaling</li>
* </ul>
*
* <p>
* Disabling post-processing forgoes color correctness as well as anti-aliasing and
* should only be used experimentally (e.g., for UI overlays).
* </p>
*
* @param enabled true enables post processing, false disables it
*
* @see #setToneMapping
* @see #setAntiAliasing
* @see #setDithering
* @see #setSampleCount
*/
public void setPostProcessingEnabled(boolean enabled) {
nSetPostProcessingEnabled(getNativeObject(), enabled);
}
/**
* Returns true if post-processing is enabled.
*
* @see #setPostProcessingEnabled
*/
public boolean isFrontFaceWindingInverted() {
return nIsFrontFaceWindingInverted(getNativeObject());
}
/**
* Inverts the winding order of front faces. By default front faces use a counter-clockwise
* winding order. When the winding order is inverted, front faces are faces with a clockwise
* winding order.
*
* Changing the winding order will directly affect the culling mode in materials
* (see Material#getCullingMode).
*
* Inverting the winding order of front faces is useful when rendering mirrored reflections
* (water, mirror surfaces, front camera in AR, etc.).
*
* @param inverted True to invert front faces, false otherwise.
*/
public void setFrontFaceWindingInverted(boolean inverted) {
nSetFrontFaceWindingInverted(getNativeObject(), inverted);
}
/**
* Sets options relative to dynamic lighting for this view.
*
* <p>
* Together <code>zLightNear</code> and <code>zLightFar</code> must be chosen so that the
* visible influence of lights is spread between these two values.
* </p>
*
* @param zLightNear Distance from the camera where the lights are expected to shine.
* This parameter can affect performance and is useful because depending
* on the scene, lights that shine close to the camera may not be
* visible -- in this case, using a larger value can improve performance.
* e.g. when standing and looking straight, several meters of the ground
* isn't visible and if lights are expected to shine there, there is no
* point using a short zLightNear. (Default 5m).
*
* @param zLightFar Distance from the camera after which lights are not expected to be visible.
* Similarly to zLightNear, setting this value properly can improve
* performance. (Default 100m).
*
*/
public void setDynamicLightingOptions(float zLightNear, float zLightFar) {
nSetDynamicLightingOptions(getNativeObject(), zLightNear, zLightFar);
}
/**
* Activates or deactivates ambient occlusion.
*
* @param ao Type of ambient occlusion to use.
*/
public void setAmbientOcclusion(@NonNull AmbientOcclusion ao) {
nSetAmbientOcclusion(getNativeObject(), ao.ordinal());
}
/**
* Queries the type of ambient occlusion active for this View.
*
* @return ambient occlusion type.
*/
@NonNull
public AmbientOcclusion getAmbientOcclusion() {
return AmbientOcclusion.values()[nGetAmbientOcclusion(getNativeObject())];
}
/**
* Sets ambient occlusion options.
*
* @param options Options for ambient occlusion.
*/
public void setAmbientOcclusionOptions(@NonNull AmbientOcclusionOptions options) {
mAmbientOcclusionOptions = options;
nSetAmbientOcclusionOptions(getNativeObject(), options.radius, options.bias, options.power, options.resolution);
}
/**
* Gets the ambient occlusion options.
*
* @return ambient occlusion options currently set.
*/
@NonNull
public AmbientOcclusionOptions getAmbientOcclusionOptions() {
if (mAmbientOcclusionOptions == null) {

View File

@@ -18,6 +18,16 @@ package com.google.android.filament;
import android.support.annotation.IntRange;
/**
* Specifies a rectangular region within a render target in terms of pixel coordinates.
*
* <p>
* The rectangle spans from <code>(left,bottom)</code> to <code>(left+width-1, top+height-1)</code>,
* inclusive. Width and height must be non-negative.
* </p>
*
* @see View#setViewport
*/
public class Viewport {
public Viewport(int left, int bottom, @IntRange(from = 0) int width, @IntRange(from = 0) int height) {
this.left = left;

View File

@@ -34,21 +34,6 @@ public final class TextureHelper {
private static final int BITMAP_CONFIG_RGBA_F16 = 4;
private static final int BITMAP_CONFIG_HARDWARE = 5;
private static Method sEngineGetNativeObject;
private static Method sTextureGetNativeObject;
static {
try {
sEngineGetNativeObject = Engine.class.getDeclaredMethod("getNativeObject");
sTextureGetNativeObject = Texture.class.getDeclaredMethod("getNativeObject");
sEngineGetNativeObject.setAccessible(true);
sTextureGetNativeObject.setAccessible(true);
} catch (NoSuchMethodException e) {
// Cannot happen
}
}
private TextureHelper() {
}
@@ -69,14 +54,10 @@ public final class TextureHelper {
throw new IllegalArgumentException("Unsupported config: ARGB_4444 or HARDWARE");
}
try {
long nativeTexture = (Long) sTextureGetNativeObject.invoke(texture);
long nativeEngine = (Long) sEngineGetNativeObject.invoke(engine);
nSetBitmap(nativeTexture, nativeEngine, level, xoffset, yoffset, width, height,
bitmap, format);
} catch (Exception e) {
// Ignored
}
long nativeTexture = texture.getNativeObject();
long nativeEngine = engine.getNativeObject();
nSetBitmap(nativeTexture, nativeEngine, level, xoffset, yoffset, width, height,
bitmap, format);
}
private static int toNativeFormat(Bitmap.Config config) {

View File

@@ -99,10 +99,6 @@ import com.google.android.filament.SwapChain;
* // Always detach the surface before destroying the engine
* mUiHelper.detach();
*
* // This ensures that all the commands we've sent to Filament have
* // been processed before we attempt to destroy anything
* Fence.waitAndDestroy(mEngine.createFence(Fence.Type.SOFT), Fence.Mode.FLUSH);
*
* mEngine.destroy();
* }
*

View File

@@ -26,16 +26,14 @@ import java.lang.annotation.Target;
*
* Note that adding this annotation to a method is not enough to guarantee that
* it is kept - either its class must be referenced elsewhere in the program, or
* the class must be annotated with this as well.
* the class must be annotated with this as well. Usage example:
* <pre>
*
* Usage example:<br />
* {@code
* @UsedByNative("NativeCrashHandler.cpp")
public static void reportCrash(int signal, int code, int address) {
...
}
}
* &commat;UsedByNative("NativeCrashHandler.cpp")
* public static void reportCrash(int signal, int code, int address) {
* ...
* }
* </pre>
*/
@Target({
ElementType.METHOD,

View File

@@ -26,16 +26,14 @@ import java.lang.annotation.Target;
*
* Note that adding this annotation to a method is not enough to guarantee that
* it is kept - either its class must be referenced elsewhere in the program, or
* the class must be annotated with this as well.
* the class must be annotated with this as well. Usage example:
* <pre>
*
* Usage example:<br />
* {@code
* @UsedByReflection("PeopleListItemView.java")
public PeopleListItemViewV11(Context context) {
super(context);
}
}
* &commat;UsedByReflection("PeopleListItemView.java")
* public PeopleListItemViewV11(Context context) {
* super(context);
* }
* </pre>
*/
@Target({
ElementType.METHOD,

View File

@@ -2,19 +2,22 @@ cmake_minimum_required(VERSION 3.6)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
add_library(filament SHARED IMPORTED)
set(DISABLE_FILAMENT_JNI TRUE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../filament-android ${CMAKE_CURRENT_BINARY_DIR}/filament-android)
add_library(filament STATIC IMPORTED)
set_target_properties(filament PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament.a)
add_library(backend SHARED IMPORTED)
add_library(backend STATIC IMPORTED)
set_target_properties(backend PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbackend.a)
add_library(utils SHARED IMPORTED)
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(filaflat SHARED IMPORTED)
add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
@@ -26,27 +29,27 @@ add_library(ibl STATIC IMPORTED)
set_target_properties(ibl PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl.a)
add_library(geometry SHARED IMPORTED)
add_library(geometry STATIC IMPORTED)
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
add_library(filabridge SHARED IMPORTED)
add_library(filabridge STATIC IMPORTED)
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
add_library(gltfio SHARED IMPORTED)
add_library(gltfio STATIC IMPORTED)
set_target_properties(gltfio PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_core.a)
add_library(gltfio_resources SHARED IMPORTED)
add_library(gltfio_resources STATIC IMPORTED)
set_target_properties(gltfio_resources PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_resources.a)
add_library(bluevk SHARED IMPORTED)
add_library(bluevk STATIC IMPORTED)
set_target_properties(bluevk PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbluevk.a)
add_library(smol-v SHARED IMPORTED)
add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
@@ -54,25 +57,20 @@ include_directories(${FILAMENT_DIR}/include
..
../../libs/utils/include)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-stack-protector")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffast-math -ffp-contract=fast")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility-inlines-hidden")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer -ffunction-sections -fdata-sections")
set(CMAKE_SHARED_LINKER_FLAGS " ${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
set(CMAKE_SHARED_LINKER_FLAGS " ${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic-functions")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_SOURCE_DIR}/libgltfio-jni.map")
add_library(gltfio-jni SHARED
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
src/main/cpp/FilamentAsset.cpp
src/main/cpp/KtxLoader.cpp
src/main/cpp/MaterialProvider.cpp
src/main/cpp/ResourceLoader.cpp
../common/CallbackUtils.cpp
../common/NioUtils.cpp
$<TARGET_OBJECTS:filament-jni-obj>
)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS
@@ -80,21 +78,22 @@ set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(gltfio-jni
android
gltfio
gltfio_resources
filament
filabridge
backend
filaflat
filabridge
geometry
image
ibl
utils
log
GLESv3
EGL
android
jnigraphics
gltfio_resources
m
log
)
if (FILAMENT_SUPPORTS_VULKAN)

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <gltfio/Animator.h>
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_Animator_nApplyAnimation(JNIEnv*, jclass, jlong nativeAnimator,
jint index, jfloat time) {
Animator* animator = (Animator*) nativeAnimator;
animator->applyAnimation(index, time);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_Animator_nUpdateBoneMatrices(JNIEnv*, jclass, jlong nativeAnimator) {
Animator* animator = (Animator*) nativeAnimator;
animator->updateBoneMatrices();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_Animator_nGetAnimationCount(JNIEnv*, jclass, jlong nativeAnimator) {
Animator* animator = (Animator*) nativeAnimator;
return animator->getAnimationCount();
}
extern "C" JNIEXPORT float JNICALL
Java_com_google_android_filament_gltfio_Animator_nGetAnimationDuration(JNIEnv*, jclass,
jlong nativeAnimator, jint index) {
Animator* animator = (Animator*) nativeAnimator;
return animator->getAnimationDuration(index);
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_android_filament_gltfio_Animator_nGetAnimationName(JNIEnv* env, jclass,
jlong nativeAnimator, jint index) {
Animator* animator = (Animator*) nativeAnimator;
const char* val = animator->getAnimationName(index);
return val ? env->NewStringUTF(val) : nullptr;
}

View File

@@ -71,3 +71,10 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetName(JNIEnv* env, jcla
const char* val = asset->getName(*entity);
return val ? env->NewStringUTF(val) : nullptr;
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* env, jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return (jlong) asset->getAnimator();
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.gltfio;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Updates matrices according to glTF <code>animation</code> and <code>skin</code> definitions.
*
* <p>Animator can be used for two things:
* <ul>
* <li>Updating matrices in <code>TransformManager</code> components according to glTF <code>animation</code> definitions.</li>
* <li>Updating bone matrices in <code>RenderableManager</code> components according to glTF <code>skin</code> definitions.</li>
* </ul>
* </p>
*
* @see AssetLoader
* @see FilamentAsset
* @see ResourceLoader
*/
public class Animator {
private long mNativeObject;
Animator(long nativeObject) {
mNativeObject = nativeObject;
}
/**
* Applies rotation, translation, and scale to entities that have been targeted by the given
* animation definition. Uses <code>TransformManager</code>.
*
* @param animationIndex Zero-based index for the <code>animation</code> of interest.
* @param time Elapsed time of interest in seconds.
*
* @see #getAnimationCount
*/
public void applyAnimation(@IntRange(from = 0) int animationIndex, float time) {
nApplyAnimation(mNativeObject, animationIndex, time);
}
/**
* Computes root-to-node transforms for all bone nodes, then passes
* the results into {@see RenderableManager#setBones}.
* Uses <code>TransformManager</code> and <code>RenderableManager</code>.
*
* <p>NOTE: this operation is independent of <code>animation</code>.</p>
*/
public void updateBoneMatrices() {
nUpdateBoneMatrices(mNativeObject);
}
/**
* Returns the number of <code>animation</code> definitions in the glTF asset.
*/
public int getAnimationCount() {
return nGetAnimationCount(mNativeObject);
}
/**
* Returns the duration of the specified glTF <code>animation</code> in seconds.
*
* @param animationIndex Zero-based index for the <code>animation</code> of interest.
*
* @see #getAnimationCount
* */
public float getAnimationDuration(@IntRange(from = 0) int animationIndex) {
return nGetAnimationDuration(mNativeObject, animationIndex);
}
/**
* Returns a weak reference to the string name of the specified <code>animation</code>, or an
* empty string if none was specified.
*
* @param animationIndex Zero-based index for the <code>animation</code> of interest.
*
* @see #getAnimationCount
*/
public String getAnimationName(@IntRange(from = 0) int animationIndex) {
return nGetAnimationName(mNativeObject, animationIndex);
}
private static native void nApplyAnimation(long nativeAnimator, int index, float time);
private static native void nUpdateBoneMatrices(long nativeAnimator);
private static native int nGetAnimationCount(long nativeAnimator);
private static native float nGetAnimationDuration(long nativeAnimator, int index);
private static native String nGetAnimationName(long nativeAnimator, int index);
}

View File

@@ -21,12 +21,7 @@ import android.support.annotation.Nullable;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndirectLight;
import com.google.android.filament.Skybox;
import com.google.android.filament.Texture;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.Buffer;
/**
@@ -60,45 +55,24 @@ import java.nio.Buffer;
* }
*
* ResourceLoader(engine).loadResources(filamentAsset).destroy()
* animator = asset.getAnimator()
*
* scene.addEntities(filamentAsset.entities)
* }
* </pre>
*
* @see Animator
* @see FilamentAsset
* @see ResourceLoader
*/
public class AssetLoader {
private long mNativeObject;
static Method sEngineGetNativeObject;
static Method sEntityManagerGetNativeObject;
static Constructor<Texture> sTextureConstructor;
static Constructor<IndirectLight> sIndirectLightConstructor;
static Constructor<Skybox> sSkyboxConstructor;
/**
* Initializes the gltfio JNI layer. Must be called before using any gltfio functionality.
*/
public static void init() {
System.loadLibrary("gltfio-jni");
try {
sEngineGetNativeObject = Engine.class.getDeclaredMethod("getNativeObject");
sEngineGetNativeObject.setAccessible(true);
sEntityManagerGetNativeObject = EntityManager.class.getDeclaredMethod("getNativeObject");
sEntityManagerGetNativeObject.setAccessible(true);
sTextureConstructor = Texture.class.getDeclaredConstructor(long.class);
sTextureConstructor.setAccessible(true);
sIndirectLightConstructor = IndirectLight.class.getDeclaredConstructor(long.class);
sIndirectLightConstructor.setAccessible(true);
sSkyboxConstructor = Skybox.class.getDeclaredConstructor(long.class);
sSkyboxConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
// Cannot happen
}
}
/**
@@ -111,14 +85,12 @@ public class AssetLoader {
*/
public AssetLoader(@NonNull Engine engine, @NonNull MaterialProvider generator,
@NonNull EntityManager entities) {
try {
long nativeEngine = (long) sEngineGetNativeObject.invoke(engine);
long nativeMaterials = generator.getNativeObject();
long nativeEntities = (long) sEntityManagerGetNativeObject.invoke(entities);
mNativeObject = nCreateAssetLoader(nativeEngine, nativeMaterials, nativeEntities);
} catch (Exception e) {
// Ignored
}
long nativeEngine = engine.getNativeObject();
long nativeMaterials = generator.getNativeObject();
long nativeEntities = entities.getNativeObject();
mNativeObject = nCreateAssetLoader(nativeEngine, nativeMaterials, nativeEntities);
if (mNativeObject == 0) {
throw new IllegalStateException("Unable to parse glTF asset.");
}

View File

@@ -37,16 +37,17 @@ import com.google.android.filament.Entity;
* <p>Clients can use {@link ResourceLoader} to create textures, compute tangent quaternions, and
* upload data into vertex buffers and index buffers.</p>
*
* <p>TODO: <code>Animator</code> is not yet exposed to Java / Kotlin clients.</p>
*
* @see ResourceLoader
* @see Animator
* @see AssetLoader
*/
public class FilamentAsset {
private long mNativeObject;
private Animator mAnimator;
FilamentAsset(long nativeObject) {
mNativeObject = nativeObject;
mAnimator = null;
}
long getNativeObject() {
@@ -82,12 +83,26 @@ public class FilamentAsset {
}
/**
* Gets the <code>NameComponentManager<?code> label for the given entity, if it exists.
* Gets the <code>NameComponentManager</code> label for the given entity, if it exists.
*/
public String getName(@Entity int entity) {
return nGetName(getNativeObject(), entity);
}
/**
* Creates or retrieves the <code>Animator</code> for this asset.
*
* <p>When calling this for the first time, this must be called after
* {@see ResourceLoader#loadResources}.</p>
*/
public Animator getAnimator() {
if (mAnimator != null) {
return mAnimator;
}
mAnimator = new Animator(nGetAnimator(getNativeObject()));
return mAnimator;
}
void clearNativeObject() {
mNativeObject = 0;
}
@@ -97,4 +112,5 @@ public class FilamentAsset {
private static native void nGetEntities(long nativeAsset, int[] result);
private static native void nGetBoundingBox(long nativeAsset, float[] box);
private static native String nGetName(long nativeAsset, int entity);
private static native long nGetAnimator(long nativeAsset);
}

View File

@@ -24,6 +24,8 @@ import com.google.android.filament.IndirectLight;
import com.google.android.filament.Skybox;
import com.google.android.filament.Texture;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.Buffer;
/**
@@ -33,6 +35,24 @@ import java.nio.Buffer;
* into a single file.</p>
*/
public class KtxLoader {
private static Constructor<Texture> sTextureConstructor;
private static Constructor<IndirectLight> sIndirectLightConstructor;
private static Constructor<Skybox> sSkyboxConstructor;
static {
try {
sTextureConstructor = Texture.class.getDeclaredConstructor(long.class);
sTextureConstructor.setAccessible(true);
sIndirectLightConstructor = IndirectLight.class.getDeclaredConstructor(long.class);
sIndirectLightConstructor.setAccessible(true);
sSkyboxConstructor = Skybox.class.getDeclaredConstructor(long.class);
sSkyboxConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
// Cannot happen
}
}
public static class Options {
public boolean srgb;
@@ -49,9 +69,9 @@ public class KtxLoader {
@Nullable
public static Texture createTexture(@NonNull Engine engine, @NonNull Buffer buffer, @NonNull Options options) {
try {
long nativeEngine = (long) AssetLoader.sEngineGetNativeObject.invoke(engine);
long nativeEngine = engine.getNativeObject();
long nativeTexture = nCreateTexture(nativeEngine, buffer, buffer.remaining(), options.srgb);
return AssetLoader.sTextureConstructor.newInstance(nativeTexture);
return sTextureConstructor.newInstance(nativeTexture);
} catch (Exception e) {
return null;
}
@@ -68,9 +88,9 @@ public class KtxLoader {
@Nullable
public static IndirectLight createIndirectLight(@NonNull Engine engine, @NonNull Buffer buffer, @NonNull Options options) {
try {
long nativeEngine = (long) AssetLoader.sEngineGetNativeObject.invoke(engine);
long nativeEngine = engine.getNativeObject();
long nativeIndirectLight = nCreateIndirectLight(nativeEngine, buffer, buffer.remaining(), options.srgb);
return AssetLoader.sIndirectLightConstructor.newInstance(nativeIndirectLight);
return sIndirectLightConstructor.newInstance(nativeIndirectLight);
} catch (Exception e) {
return null;
}
@@ -87,9 +107,9 @@ public class KtxLoader {
@Nullable
public static Skybox createSkybox(@NonNull Engine engine, @NonNull Buffer buffer, @NonNull Options options) {
try {
long nativeEngine = (long) AssetLoader.sEngineGetNativeObject.invoke(engine);
long nativeEngine = engine.getNativeObject();
long nativeSkybox = nCreateSkybox(nativeEngine, buffer, buffer.remaining(), options.srgb);
return AssetLoader.sSkyboxConstructor.newInstance(nativeSkybox);
return sSkyboxConstructor.newInstance(nativeSkybox);
} catch (Exception e) {
return null;
}

View File

@@ -29,29 +29,14 @@ import java.lang.reflect.Method;
public class MaterialProvider {
private long mNativeObject;
private static Method sEngineGetNativeObject;
static {
try {
sEngineGetNativeObject = Engine.class.getDeclaredMethod("getNativeObject");
sEngineGetNativeObject.setAccessible(true);
} catch (NoSuchMethodException e) {
// Cannot happen
}
}
/**
* Constructs an ubershader loader using the supplied {@link Engine}.
*
* @param engine the engine used to create materials
*/
public MaterialProvider(Engine engine) {
try {
long nativeEngine = (long) sEngineGetNativeObject.invoke(engine);
mNativeObject = nCreateMaterialProvider(nativeEngine);
} catch (Exception e) {
// Ignored
}
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateMaterialProvider(nativeEngine);
}
/**

View File

@@ -36,17 +36,6 @@ import java.nio.Buffer;
public class ResourceLoader {
private final long mNativeObject;
private static Method sEngineGetNativeObject;
static {
try {
sEngineGetNativeObject = Engine.class.getDeclaredMethod("getNativeObject");
sEngineGetNativeObject.setAccessible(true);
} catch (NoSuchMethodException e) {
// Cannot happen
}
}
/**
* Constructs a resource loader tied to the given Filament engine.
*
@@ -54,8 +43,8 @@ public class ResourceLoader {
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public ResourceLoader(@NonNull Engine engine) throws IllegalAccessException, InvocationTargetException {
long nativeEngine = (long) sEngineGetNativeObject.invoke(engine);
public ResourceLoader(@NonNull Engine engine) {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine);
}

View File

@@ -52,6 +52,12 @@ Demonstrates how to load glTF models and render to an offscreen buffer:
![glTF Bloom](../../docs/images/samples/sample_gltf_bloom.jpg)
### `hello-camera`
Demonstrates how to use `Stream` with Android's Camera2 API:
![Hello Camera](../../docs/images/samples/sample_hello_camera.jpg)
## Prerequisites
Before you start, make sure to read [Filament's README](../../README.md). You need to be able to

View File

@@ -43,10 +43,9 @@ data class Framebuffer(
class MainActivity : Activity() {
// Be sure to initialize not only Filament, but also gltfio (via AssetLoader)
// We are using the gltfio library, so init the AssetLoader rather than Filament.
companion object {
init {
Filament.init()
AssetLoader.init()
}
}
@@ -155,7 +154,7 @@ class MainActivity : Activity() {
// IndirectLight and SkyBox
// ------------------------
val ibl = "venetian_crossroads_2k";
val ibl = "venetian_crossroads_2k"
readUncompressedAsset("envs/$ibl/${ibl}_ibl.ktx").let {
primary.scene.indirectLight = KtxLoader.createIndirectLight(engine, it, KtxLoader.Options())
@@ -209,7 +208,7 @@ class MainActivity : Activity() {
}
}
// Punctual Light Sources
// Light Sources
// ----------------------
light = EntityManager.get().create()
@@ -236,12 +235,19 @@ class MainActivity : Activity() {
tm.setTransform(tm.getInstance(filamentAsset.root),
floatArrayOf(
cos(v), 0.0f, -sin(v), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
sin(v), 0.0f, cos(v), 0.0f,
0.0f, -1.7f, 0.0f, 1.0f
cos(v), 0.0f, -sin(v), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
sin(v), 0.0f, cos(v), 0.0f,
0.0f, -1.7f, 0.0f, 1.0f
))
// The lucy asset does not have animation but we invoke the asset animator for demonstration purposes.
if (filamentAsset.animator.animationCount > 0) {
val elapsedTimeInSeconds = a.currentPlayTime.toFloat() / 1000.0f
filamentAsset.animator.applyAnimation(0, elapsedTimeInSeconds)
filamentAsset.animator.updateBoneMatrices()
}
}
animator.start()
}
@@ -429,15 +435,11 @@ class MainActivity : Activity() {
// Stop the animation and any pending frame
choreographer.removeFrameCallback(frameScheduler)
animator.cancel();
animator.cancel()
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
assetLoader.destroyAsset(filamentAsset)
assetLoader.destroy()

12
android/samples/hello-camera/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/.idea/caches
/.idea/gradle.xml
.DS_Store
/build
/captures
/app/src/main/assets/materials/*.filamat
.externalNativeBuild

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply from: '../../../build/filament-tasks.gradle'
compileMaterials {
group 'Filament'
description 'Compile materials'
inputDir = file("src/main/materials")
outputDir = file("src/main/assets/materials")
}
preBuild.dependsOn compileMaterials
clean.doFirst {
delete "src/main/assets"
}
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.google.android.filament.hellocam"
minSdkVersion 23 // 21 is required for CameraDevice.StateCallback, 23 is required for shouldShowRequestPermissionRationale
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Filament comes with native code, the following declarations
// can be used to generate architecture specific APKs
flavorDimensions 'cpuArch'
productFlavors {
arm8 {
dimension 'cpuArch'
ndk {
abiFilters 'arm64-v8a'
}
}
arm7 {
dimension 'cpuArch'
ndk {
abiFilters 'armeabi-v7a'
}
}
x86_64 {
dimension 'cpuArch'
ndk {
abiFilters 'x86_64'
}
}
x86 {
dimension 'cpuArch'
ndk {
abiFilters 'x86'
}
}
universal {
dimension 'cpuArch'
}
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {
noCompress 'filamat'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Depend on Filament
implementation 'com.google.android.filament:filament-android'
implementation 'com.android.support:support-compat:28.0.0'
}

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.hellocam">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.hellocam
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.hardware.camera2.*
import android.os.Handler
import android.os.HandlerThread
import android.support.v4.content.ContextCompat
import android.util.Log
import android.util.Size
import android.view.Surface
import android.Manifest
import android.opengl.Matrix
import android.view.Display
import com.google.android.filament.*
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
/**
* Toy class that handles all interaction with the Android camera2 API.
* Sets the "textureTransform" and "videoTexture" parameters on the given Filament material.
*/
class CameraHelper(val activity: Activity, private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance, private val display: Display) {
private lateinit var cameraId: String
private lateinit var captureRequest: CaptureRequest
private val cameraOpenCloseLock = Semaphore(1)
private var backgroundHandler: Handler? = null
private var backgroundThread: HandlerThread? = null
private var cameraDevice: CameraDevice? = null
private var captureSession: CameraCaptureSession? = null
private var resolution = Size(640, 480)
private var filamentTexture: Texture? = null
private var filamentStream: Stream? = null
private var surfaceTexture: SurfaceTexture? = null
private val cameraCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
this@CameraHelper.cameraDevice = cameraDevice
createCaptureSession()
}
override fun onDisconnected(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
cameraDevice.close()
this@CameraHelper.cameraDevice = null
}
override fun onError(cameraDevice: CameraDevice, error: Int) {
onDisconnected(cameraDevice)
this@CameraHelper.activity.finish()
}
}
/**
* Finds the front-facing Android camera, requests permission, and sets up a listener that will
* start a capture session as soon as the camera is ready.
*/
fun openCamera() {
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
for (cameraId in manager.cameraIdList) {
val characteristics = manager.getCameraCharacteristics(cameraId)
val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)
if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) {
continue
}
this.cameraId = cameraId
Log.i(kLogTag, "Selected camera $cameraId.")
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
resolution = map.getOutputSizes(SurfaceTexture::class.java)[0]
Log.i(kLogTag, "Highest resolution is $resolution.")
}
} catch (e: CameraAccessException) {
Log.e(kLogTag, e.toString())
} catch (e: NullPointerException) {
Log.e(kLogTag, "Camera2 API is not supported on this device.")
}
val permission = ContextCompat.checkSelfPermission(this.activity, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(arrayOf(Manifest.permission.CAMERA), kRequestCameraPermission)
return
}
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw RuntimeException("Time out waiting to lock camera opening.")
}
manager.openCamera(cameraId, cameraCallback, backgroundHandler)
}
fun onResume() {
backgroundThread = HandlerThread("CameraBackground").also { it.start() }
backgroundHandler = Handler(backgroundThread?.looper!!)
}
fun onPause() {
backgroundThread?.quitSafely()
try {
backgroundThread?.join()
backgroundThread = null
backgroundHandler = null
} catch (e: InterruptedException) {
Log.e(kLogTag, e.toString())
}
}
fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray): Boolean {
if (requestCode == kRequestCameraPermission) {
if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Log.e(kLogTag, "Unable to obtain camera position.")
}
return true
}
return false
}
private fun createCaptureSession() {
if (surfaceTexture != null) {
surfaceTexture!!.release()
filamentEngine.destroyStream(filamentStream!!)
}
// Create the Android surface that will hold the camera image.
surfaceTexture = SurfaceTexture(0)
surfaceTexture!!.setDefaultBufferSize(resolution.width, resolution.height)
surfaceTexture!!.detachFromGLContext()
val surface = Surface(surfaceTexture)
// [Re]create the Filament Stream object that gets bound to the Texture.
filamentStream = Stream.Builder()
.stream(surfaceTexture!!)
.build(filamentEngine)
// Create the Filament Texture object if we haven't done so already.
if (filamentTexture == null) {
filamentTexture = Texture.Builder()
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
.format(Texture.InternalFormat.RGB8)
.build(filamentEngine)
}
// We are texturing a front-facing square shape so we need to generate a matrix that transforms (u, v, 0, 1)
// into a new UV coordinate according to the screen rotation and the aspect ratio of the camera image.
val aspectRatio = resolution.width.toFloat() / resolution.height.toFloat()
val textureTransform = FloatArray(16)
Matrix.setIdentityM(textureTransform, 0)
when (display.rotation) {
Surface.ROTATION_0 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 90.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f, 1.0f / aspectRatio, 1.0f)
}
Surface.ROTATION_90 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 1.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 180.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
Surface.ROTATION_270 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
}
// Connect the Stream to the Texture and the Texture to the MaterialInstance.
val sampler = TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR, TextureSampler.WrapMode.CLAMP_TO_EDGE)
filamentTexture!!.setExternalStream(filamentEngine, filamentStream!!)
filamentMaterial.setParameter("videoTexture", filamentTexture!!, sampler)
filamentMaterial.setParameter("textureTransform", MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
// Start the capture session. You could also use TEMPLATE_PREVIEW here.
val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
captureRequestBuilder.addTarget(surface)
cameraDevice?.createCaptureSession(listOf(surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
if (cameraDevice == null) return
captureSession = cameraCaptureSession
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
captureRequest = captureRequestBuilder.build()
captureSession!!.setRepeatingRequest(captureRequest, null, backgroundHandler)
Log.i(kLogTag, "Created CaptureRequest.")
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Log.e(kLogTag, "onConfigureFailed")
}
}, null)
}
companion object {
private const val kLogTag = "CameraHelper"
private const val kRequestCameraPermission = 1
}
}

View File

@@ -0,0 +1,417 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.hellocam
import android.animation.ValueAnimator
import android.app.Activity
import android.opengl.Matrix
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.util.Log
import android.view.Choreographer
import android.view.Display
import android.view.Surface
import android.view.SurfaceView
import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.*
import com.google.android.filament.VertexBuffer.*
import com.google.android.filament.android.UiHelper
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.Channels
import kotlin.math.*
class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallback {
companion object {
init {
Filament.init()
}
}
private lateinit var surfaceView: SurfaceView
private lateinit var uiHelper: UiHelper
private lateinit var choreographer: Choreographer
private lateinit var engine: Engine
private lateinit var renderer: Renderer
private lateinit var scene: Scene
private lateinit var view: View
// This helper wraps the Android camera2 API and connects it to a Filament material.
private lateinit var cameraHelper: CameraHelper
// This is the Filament camera, not the phone camera. :)
private lateinit var camera: Camera
// Other Filament objects:
private lateinit var material: Material
private lateinit var materialInstance: MaterialInstance
private lateinit var vertexBuffer: VertexBuffer
private lateinit var indexBuffer: IndexBuffer
// Filament entity representing a renderable object
@Entity private var renderable = 0
@Entity private var light = 0
// A swap chain is Filament's representation of a surface
private var swapChain: SwapChain? = null
// Performs the rendering and schedules new frames
private val frameScheduler = FrameCallback()
private val animator = ValueAnimator.ofFloat(0.0f, 50.0f)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
surfaceView = SurfaceView(this)
setContentView(surfaceView)
choreographer = Choreographer.getInstance()
setupSurfaceView()
setupFilament()
setupView()
setupScene()
cameraHelper = CameraHelper(this, engine, materialInstance, windowManager.defaultDisplay)
cameraHelper.openCamera()
}
private fun setupSurfaceView() {
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
uiHelper.renderCallback = SurfaceCallback()
uiHelper.attachTo(surfaceView)
}
private fun setupFilament() {
engine = Engine.create()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
camera = engine.createCamera()
}
private fun setupView() {
view.setClearColor(0.035f, 0.035f, 0.035f, 1.0f)
view.camera = camera
view.scene = scene
}
private fun setupScene() {
loadMaterial()
setupMaterial()
createMesh()
// To create a renderable we first create a generic entity
renderable = EntityManager.get().create()
// We then create a renderable component on that entity
// A renderable is made of several primitives; in this case we declare only 1
// If we wanted each face of the cube to have a different material, we could
// declare 6 primitives (1 per face) and give each of them a different material
// instance, setup with different parameters
RenderableManager.Builder(1)
// Overall bounding box of the renderable
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f))
// Sets the mesh data of the first primitive, 6 faces of 6 indices each
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 6 * 6)
// Sets the material of the first primitive
.material(0, materialInstance)
.build(engine, renderable)
// Add the entity to the scene to render it
scene.addEntity(renderable)
// We now need a light, let's create a directional light
light = EntityManager.get().create()
// Create a color from a temperature (5,500K)
val (r, g, b) = Colors.cct(5_500.0f)
LightManager.Builder(LightManager.Type.DIRECTIONAL)
.color(r, g, b)
// Intensity of the sun in lux on a clear day
.intensity(110_000.0f)
// The direction is normalized on our behalf
.direction(0.0f, -0.5f, -1.0f)
.castShadows(true)
.build(engine, light)
// Add the entity to the scene to light it
scene.addEntity(light)
// Set the exposure on the camera, this exposure follows the sunny f/16 rule
// Since we've defined a light that has the same intensity as the sun, it
// guarantees a proper exposure
camera.setExposure(16.0f, 1.0f / 125.0f, 100.0f)
// Move the camera back to see the object
camera.lookAt(0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
startAnimation()
}
private fun loadMaterial() {
readUncompressedAsset("materials/lit.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
}
}
private fun setupMaterial() {
materialInstance = material.createInstance()
materialInstance.setParameter("baseColor", Colors.RgbType.SRGB, 1.0f, 0.85f, 0.57f)
materialInstance.setParameter("roughness", 0.3f)
}
private fun createMesh() {
val floatSize = 4
val shortSize = 2
// A vertex is a position + a tangent frame:
// 3 floats for XYZ position, 4 floats for normal+tangents (quaternion)
val vertexSize = 3 * floatSize + 4 * floatSize
// Define a vertex and a function to put a vertex in a ByteBuffer
@Suppress("ArrayInDataClass")
data class Vertex(val x: Float, val y: Float, val z: Float, val tangents: FloatArray)
fun ByteBuffer.put(v: Vertex): ByteBuffer {
putFloat(v.x)
putFloat(v.y)
putFloat(v.z)
v.tangents.forEach { putFloat(it) }
return this
}
// 6 faces, 4 vertices per face
val vertexCount = 6 * 4
// Create tangent frames, one per face
val tfPX = FloatArray(4)
val tfNX = FloatArray(4)
val tfPY = FloatArray(4)
val tfNY = FloatArray(4)
val tfPZ = FloatArray(4)
val tfNZ = FloatArray(4)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, tfPX)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, tfNX)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tfPY)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, tfNY)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, tfPZ)
MathUtils.packTangentFrame( 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, tfNZ)
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
// It is important to respect the native byte order
.order(ByteOrder.nativeOrder())
// Face -Z
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNZ))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNZ))
// Face +X
.put(Vertex( 1.5f, -1.5f, -1.0f, tfPX))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPX))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPX))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPX))
// Face +Z
.put(Vertex(-1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPZ))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPZ))
// Face -X
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNX))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfNX))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNX))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNX))
// Face -Y
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNY))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfNY))
// Face +Y
.put(Vertex(-1.5f, 1.5f, -1.0f, tfPY))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPY))
// Make sure the cursor is pointing in the right place in the byte buffer
.flip()
// Declare the layout of our mesh
vertexBuffer = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
// Because we interleave position and color data we must specify offset and stride
// We could use de-interleaved data by declaring two buffers and giving each
// attribute a different buffer index
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.TANGENTS, 0, AttributeType.FLOAT4, 3 * floatSize, vertexSize)
.build(engine)
// Feed the vertex data to the mesh
// We only set 1 buffer because the data is interleaved
vertexBuffer.setBufferAt(engine, 0, vertexData)
// Create the indices
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
.order(ByteOrder.nativeOrder())
repeat(6) {
val i = (it * 4).toShort()
indexData
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())
.putShort(i).putShort((i + 2).toShort()).putShort((i + 3).toShort())
}
indexData.flip()
// 6 faces, 2 triangles per face,
indexBuffer = IndexBuffer.Builder()
.indexCount(vertexCount * 2)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(engine)
indexBuffer.setBuffer(engine, indexData)
}
private fun startAnimation() {
// Animate the triangle
animator.interpolator = LinearInterpolator()
animator.duration = 6000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
val transformMatrix = FloatArray(16)
override fun onAnimationUpdate(animator: ValueAnimator) {
val t = animator.animatedValue as Float
val radians = sin(t) * 3.0f * PI.toFloat()
Matrix.setRotateM(transformMatrix, 0, radians, 0.0f, 1.0f, 0.0f)
val tcm = engine.transformManager
tcm.setTransform(tcm.getInstance(renderable), transformMatrix)
}
})
animator.start()
}
override fun onResume() {
super.onResume()
choreographer.postFrameCallback(frameScheduler)
animator.start()
cameraHelper.onResume()
}
override fun onPause() {
super.onPause()
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
cameraHelper.onPause()
}
override fun onDestroy() {
super.onDestroy()
// Stop the animation and any pending frame
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
// Always detach the surface before destroying the engine
uiHelper.detach()
// Cleanup all resources
engine.destroyEntity(light)
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)
engine.destroyVertexBuffer(vertexBuffer)
engine.destroyIndexBuffer(indexBuffer)
engine.destroyMaterialInstance(materialInstance)
engine.destroyMaterial(material)
engine.destroyView(view)
engine.destroyScene(scene)
engine.destroyCamera(camera)
// Engine.destroyEntity() destroys Filament related resources only
// (components), not the entity itself
val entityManager = EntityManager.get()
entityManager.destroy(light)
entityManager.destroy(renderable)
// Destroying the engine will free up any resource you may have forgotten
// to destroy, but it's recommended to do the cleanup properly
engine.destroy()
}
inner class FrameCallback : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
// Schedule the next frame
choreographer.postFrameCallback(this)
// This check guarantees that we have a swap chain
if (uiHelper.isReadyToRender) {
// If beginFrame() returns false you should skip the frame
// This means you are sending frames too quickly to the GPU
if (renderer.beginFrame(swapChain!!)) {
renderer.render(view)
renderer.endFrame()
}
}
}
}
inner class SurfaceCallback : UiHelper.RendererCallback {
override fun onNativeWindowChanged(surface: Surface) {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
}
override fun onDetachedFromSurface() {
swapChain?.let {
engine.destroySwapChain(it)
// Required to ensure we don't return before Filament is done executing the
// destroySwapChain command, otherwise Android might destroy the Surface
// too early
engine.flushAndWait()
swapChain = null
}
}
override fun onResized(width: Int, height: Int) {
val aspect = width.toDouble() / height.toDouble()
camera.setProjection(45.0, aspect, 0.1, 20.0, Camera.Fov.VERTICAL)
view.viewport = Viewport(0, 0, width, height)
}
}
private fun readUncompressedAsset(@Suppress("SameParameterValue") assetName: String): ByteBuffer {
assets.openFd(assetName).use { fd ->
val input = fd.createInputStream()
val dst = ByteBuffer.allocate(fd.length.toInt())
val src = Channels.newChannel(input)
src.read(dst)
src.close()
return dst.apply { rewind() }
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (!cameraHelper.onRequestPermissionsResult(requestCode, grantResults)) {
this.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}

View File

@@ -0,0 +1,73 @@
// Simple lit material that defines 3 parameters:
// - baseColor
// - roughness
// - metallic
//
// These parameters can be used by the application to change the appearance of the material.
//
// This source material must be compiled to a binary material using the matc tool.
// The command used to compile this material is:
// matc -p mobile -a opengl -o app/src/main/assets/lit.filamat app/src/materials/lit.mat
//
// See build.gradle for an example of how to compile materials automatically
// Please refer to the documentation for more information about matc and the materials system.
material {
name : lit,
// Dynamic lighting is enabled on this material
shadingModel : lit,
// We don't need to declare a "requires" array, lit materials
// always requires the "tangents" vertex attribute (the normal
// is required for lighting, tangent/bitangent for normal mapping
// and anisotropy)
// Custom vertex shader outputs
variables : [
uv
],
// List of parameters exposed by this material
parameters : [
// The color must be passed in linear space, not sRGB
{
type : float3,
name : baseColor
},
{
type : float,
name : roughness
},
{
type : samplerExternal,
name : videoTexture
},
{
type : mat4,
name : textureTransform
}
],
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.uv = 0.5 * (getPosition() + vec4(1));
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.roughness = materialParams.roughness;
material.metallic = 0.0;
// Apply the video stream to the +Z face on the cube.
if (variable_uv.z >= 1.0) {
vec2 uv = (materialParams.textureTransform * vec4(variable_uv.xy, 0, 1)).xy;
material.baseColor.rgb = inverseTonemapSRGB(texture(materialParams_videoTexture, uv).rgb);
} else {
material.baseColor.rgb = materialParams.baseColor;
}
}
}

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector>

View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z"/>
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Hello Camera</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,13 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Mon Jan 14 11:08:15 PST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

172
android/samples/hello-camera/gradlew vendored Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,3 @@
includeBuild '../../filament-android'
include ':app'

View File

@@ -261,10 +261,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)

View File

@@ -238,10 +238,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
destroyMesh(engine, mesh)
destroyIbl(engine, ibl)

View File

@@ -346,10 +346,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
engine.destroyEntity(light)
engine.destroyEntity(renderable)

View File

@@ -73,6 +73,13 @@ android {
}
}
// The filamat library has two variants: full and lite. Here we default to the "full" variant.
// Replace "full" with "lite" to use the filamat-lite variant, which has a smaller binary size
// but comes with certain restrictions. See "Filamat Lite" in libs/filamat/README.md.
defaultConfig {
missingDimensionStrategy 'functionality', 'full'
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -212,6 +212,10 @@ class MainActivity : Activity() {
" material.clearCoat = 1.0;\n" +
"}\n")
// Turn off shader code optimization so this sample is compatible with the "lite"
// variant of the filamat library.
.optimization(MaterialBuilder.Optimization.NONE)
.build()
if (matPackage.isValid) {
@@ -274,10 +278,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
destroyMesh(engine, mesh)
destroyIbl(engine, ibl)

View File

@@ -261,10 +261,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)

View File

@@ -257,10 +257,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
destroyMesh(engine, mesh)
destroyIbl(engine, ibl)

View File

@@ -279,10 +279,6 @@ class MainActivity : Activity() {
// Always detach the surface before destroying the engine
uiHelper.detach()
// This ensures that all the commands we've sent to Filament have
// been processed before we attempt to destroy anything
engine.flushAndWait()
// Cleanup all resources
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)

View File

@@ -29,7 +29,9 @@ cd ..
# Steps for GitHub Workflows
if [[ "$GITHUB_WORKFLOW" ]]; then
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100

View File

@@ -0,0 +1,20 @@
@echo off
if "%GITHUB_WORKFLOW%" == "" (set RUNNING_LOCALLY=1)
set VISUAL_STUDIO_VERSION="Enterprise"
if "%RUNNING_LOCALLY%" == "1" (set VISUAL_STUDIO_VERSION="Professional")
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\%VISUAL_STUDIO_VERSION%\VC\Auxiliary\Build\vcvars64.bat"
if errorlevel 1 exit /b %errorlevel%
msbuild /version
cmake --version
mkdir out\cmake-release
cd out\cmake-release
if errorlevel 1 exit /b %errorlevel%
cmake ..\.. -G "Visual Studio 16 2019" -A x64 || exit /b
msbuild TNT.sln /m /p:configuration=Release

View File

@@ -1,3 +0,0 @@
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "filament/build/windows/build.bat"

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -37,6 +37,10 @@ To use Filament from Java you must use the following two libraries instead:
- `filament-java.jar`, Contains Filament's Java classes
- `filament-jni`, Filament's JNI bindings
To link against debug builds of Filament, you must also link against:
- `matdbg`, Support library that adds an interactive web-based debugger to Filament
To use the Vulkan backend on macOS you must also make the following libraries available at runtime:
- `MoltenVK_icd.json`
- `libMoltenVK.dylib`

View File

@@ -19,6 +19,7 @@ set(PUBLIC_HDRS
set(SRCS
src/BackendUtils.cpp
src/Callable.cpp
src/CircularBuffer.cpp
src/CommandBufferQueue.cpp
src/CommandStream.cpp
@@ -276,7 +277,9 @@ install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
# ==================================================================================================
# Test
# ==================================================================================================
if (APPLE AND NOT IOS)
option(INSTALL_BACKEND_TEST "Install the backend test library so it can be consumed on iOS" OFF)
if (APPLE)
add_library(backend_test STATIC
test/BackendTest.cpp
test/ShaderGenerator.cpp
@@ -292,6 +295,34 @@ if (APPLE AND NOT IOS)
SPIRV
spirv-cross-glsl)
set(BACKEND_TEST_DEPS
OGLCompiler
OSDependent
SPIRV
SPIRV-Tools
SPIRV-Tools-opt
backend_test
filabridge
getopt
gtest
glslang
spirv-cross-core
spirv-cross-glsl
spirv-cross-msl
)
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
combine_static_libs(backend_test "${BACKEND_TEST_COMBINED_OUTPUT}" "${BACKEND_TEST_DEPS}")
set(BACKEND_TEST_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}backend_test${CMAKE_STATIC_LIBRARY_SUFFIX})
if (INSTALL_BACKEND_TEST)
install(FILES "${BACKEND_TEST_COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR} RENAME ${BACKEND_TEST_LIB_NAME})
install(FILES test/PlatformRunner.h DESTINATION include/backend_test)
endif()
endif()
if (APPLE AND NOT IOS)
add_executable(backend_test_mac test/mac_runner.mm)
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
# Because each test case is a separate file, the -force_load flag is necessary to prevent the

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_PRESENT_CALLABLE
#define TNT_FILAMENT_BACKEND_PRESENT_CALLABLE
#include <utils/compiler.h>
namespace filament {
namespace backend {
/**
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
* a SwapChain.
*
* Typically, Filament's backend is responsible scheduling a frame's presentation. However, there
* are certain cases where the application might want to control when a frame is scheduled for
* presentation.
*
* For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present
* within a CATransation:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void myFrameFinishedCallback(PresentCallable presentCallable, void* user) {
* [CATransaction begin];
* // Update other UI elements...
* presentCallable();
* [CATransaction commit];
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To obtain a PresentCallable, pass a backend::FrameFinishedCallback to the beginFrame() function.
* The callback is called with a PresentCallable object and optional user data:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* if (renderer->beginFrame(swapChain, myFrameFinishedCallback, nullptr)) {
* renderer->render(view);
* renderer->endFrame();
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
*
* @remark The backend::FrameFinishedCallback is called on an arbitrary thread.
*
* Applications *must* call each PresentCallable they receive. Each PresentCallable represents a
* frame that is waiting to be presented. If an application fails to call a PresentCallable, a
* memory leak could occur. To "cancel" the presentation of a frame, pass false to the
* PresentCallable, which will cancel the presentation of the frame and release associated memory.
*
* @see Renderer, SwapChain, Renderer.beginFrame
*/
class UTILS_PUBLIC PresentCallable {
public:
using PresentFn = void(*)(bool presentFrame, void* user);
PresentCallable(PresentFn fn, void* user) noexcept;
~PresentCallable() noexcept = default;
PresentCallable(const PresentCallable& rhs) = default;
PresentCallable& operator=(const PresentCallable& rhs) = default;
/**
* Call this PresentCallable, scheduling the associated frame for presentation. Pass false for
* presentFrame to effectively "cancel" the presentation of the frame.
*
* @param presentFrame if false, will not present the frame but releases associated memory
*/
void operator()(bool presentFrame = true) noexcept;
private:
PresentFn mPresentFn;
void* mUser = nullptr;
};
/**
* FrameFinishedCallback is a callback function that notifies an application when Filament has
* finished processing a frame and that frame is ready to be scheduled for presentation.
*
* beginFrame() takes an optional FrameFinishedCallback. If the callback is provided, then that
* frame will *not* automatically be scheduled for presentation. Instead, the application must call
* the given PresentCallable.
*
* @remark The backend::FrameFinishedCallback is called on an arbitrary thread.
*
* @see PresentCallable, beginFrame()
*/
using FrameFinishedCallback = void(*)(PresentCallable callable, void* user);
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRESENT_FRAME_CALLABLE

View File

@@ -25,6 +25,7 @@
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/PixelBufferDescriptor.h>
#include <backend/PresentCallable.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/DriverApi.h"

View File

@@ -21,6 +21,7 @@
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/PixelBufferDescriptor.h>
#include <backend/PresentCallable.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/DriverApiForward.h"

View File

@@ -110,7 +110,9 @@
DECL_DRIVER_API_N(beginFrame,
int64_t, monotonic_clock_ns,
uint32_t, frameId)
uint32_t, frameId,
backend::FrameFinishedCallback, callback,
void*, user)
DECL_DRIVER_API_N(setPresentationTime,
int64_t, monotonic_clock_ns)

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <backend/PresentCallable.h>
#include <utils/Panic.h>
namespace filament {
namespace backend {
PresentCallable::PresentCallable(PresentFn fn, void* user) noexcept
: mPresentFn(fn), mUser(user) {
assert(fn != nullptr);
}
void PresentCallable::operator()(bool presentFrame) noexcept {
ASSERT_PRECONDITION(mPresentFn, "This PresentCallable was already called. " \
"PresentCallables should be called exactly once.");
mPresentFn(presentFrame, mUser);
// Set mPresentFn to nullptr to denote that the callable has been called.
mPresentFn = nullptr;
}
} // namespace backend
} // namespace filament

View File

@@ -90,22 +90,27 @@ void* CircularBuffer::alloc(size_t size) noexcept {
if (UTILS_UNLIKELY(mUsesAshmem < 0)) {
// ashmem failed
if (vaddr_guard != MAP_FAILED)
if (vaddr_guard != MAP_FAILED) {
munmap(vaddr_guard, size);
}
if (vaddr_shadow != MAP_FAILED)
if (vaddr_shadow != MAP_FAILED) {
munmap(vaddr_shadow, size);
}
if (vaddr != MAP_FAILED)
if (vaddr != MAP_FAILED) {
munmap(vaddr, size);
}
if (fd >= 0)
if (fd >= 0) {
close(fd);
}
data = mmap(nullptr, size * 2 + BLOCK_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
data = mmap(nullptr, size * 2 + BLOCK_SIZE,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_POSTCONDITION(data,
"couldn't allocate %u KiB of memory for the command buffer",
"couldn't allocate %u KiB of virtual address space for the command buffer",
(size * 2 / 1024));
slog.d << "WARNING: Using soft CircularBuffer (" << (size * 2 / 1024) << " KiB)"

View File

@@ -53,6 +53,15 @@ struct MetalContext {
id<MTLCommandBuffer> currentCommandBuffer = nullptr;
id<MTLRenderCommandEncoder> currentCommandEncoder = nullptr;
// These two fields store a callback and user data to notify the client that a frame is ready
// for presentation.
// If frameFinishedCallback is nullptr, then the Metal backend automatically calls
// presentDrawable when the frame is commited.
// Otherwise, the Metal backend will not automatically present the frame. Instead, clients bear
// the responsibility of presenting the frame by calling the PresentCallable object.
backend::FrameFinishedCallback frameFinishedCallback = nullptr;
void* frameFinishedUserData = nullptr;
// Tracks resources used by command buffers.
MetalResourceTracker resourceTracker;

View File

@@ -24,9 +24,32 @@ namespace filament {
namespace backend {
namespace metal {
void presentDrawable(bool presentFrame, void* user) {
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>) user;
if (presentFrame) {
[drawable present];
}
[drawable release];
}
id<CAMetalDrawable> acquireDrawable(MetalContext* context) {
if (!context->currentDrawable) {
context->currentDrawable = [context->currentSurface->layer nextDrawable];
// The drawable is retained here and will be released either:
// 1. in MetalDriver::commit
// 2. in the presentDrawable function, when the client calls the PresentCallable
context->currentDrawable = [[context->currentSurface->layer nextDrawable] retain];
if (context->frameFinishedCallback) {
id<CAMetalDrawable> drawable = context->currentDrawable;
backend::FrameFinishedCallback callback = context->frameFinishedCallback;
void* userData = context->frameFinishedUserData;
[context->currentCommandBuffer addScheduledHandler:^(id<MTLCommandBuffer> cb) {
PresentCallable callable(presentDrawable, (void*) drawable);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
callback(callable, userData);
});
}];
}
}
ASSERT_POSTCONDITION(context->currentDrawable != nil, "Could not obtain drawable.");
return context->currentDrawable;

View File

@@ -89,13 +89,18 @@ void MetalDriver::debugCommand(const char *methodName) {
}
#endif
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId,
backend::FrameFinishedCallback callback, void* user) {
mContext->framePool = [[NSAutoreleasePool alloc] init];
id<MTLCommandBuffer> commandBuffer = acquireCommandBuffer(mContext);
[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
mContext->resourceTracker.clearResources(buffer);
}];
// If a callback was specified, then the client is responsible for presenting the frame.
mContext->frameFinishedCallback = callback;
mContext->frameFinishedUserData = user;
}
void MetalDriver::setPresentationTime(int64_t monotonic_clock_ns) {
@@ -115,6 +120,12 @@ void MetalDriver::endFrame(uint32_t frameId) {
[mContext->framePool drain];
mContext->bufferPool->gc();
if (!mContext->frameFinishedCallback) {
// If we're responsible for presenting the frame, then by this point we've already done so
// and it's safe to release the drawable.
[mContext->currentDrawable release];
}
CVMetalTextureCacheFlush(mContext->textureCache, 0);
}
@@ -540,11 +551,11 @@ bool MetalDriver::canGenerateMipmaps() {
void MetalDriver::loadUniformBuffer(Handle<HwUniformBuffer> ubh,
BufferDescriptor&& data) {
if (data.size <= 0) {
if (data.size <= 0) {
return;
}
}
auto buffer = handle_cast<MetalUniformBuffer>(mHandleMap, ubh);
auto buffer = handle_cast<MetalUniformBuffer>(mHandleMap, ubh);
buffer->copyIntoBuffer(data.buffer, data.size);
scheduleDestroy(std::move(data));
@@ -664,8 +675,9 @@ void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> s
}
void MetalDriver::commit(Handle<HwSwapChain> sch) {
if (mContext->currentDrawable != nil) {
if (mContext->currentDrawable != nil && !mContext->frameFinishedCallback) {
[mContext->currentCommandBuffer presentDrawable:mContext->currentDrawable];
[mContext->currentDrawable release];
}
[mContext->currentCommandBuffer commit];
mContext->currentCommandBuffer = nil;

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