Compare commits

..

112 Commits

Author SHA1 Message Date
Philip Rideout
a052d6cf49 Fix STREAM uniform buffers for web. 2018-11-06 13:50:49 -08:00
Mathias Agopian
591c9f2454 Fix a crasher when allocating the last Job
Instead of returning nullptr, the pool allocator returned an
invalid pointer.
2018-11-06 13:44:35 -08:00
Mathias Agopian
e580bfed56 Fix allocator test when --gtest_repeat is used 2018-11-06 13:21:33 -08:00
Mathias Agopian
e2896f4632 Set cache size to 64 bytes on all platforms
We used to assume 32-bytes cache lines when running on ARM 32-bit mode,
however, that was wrong because 32-bit mode doesn't change the cpu's
cache line. On all modern platforms we support, the cache line
is 64 bytes.

Set Job storage to 48 bytes on all platforms.
2018-11-06 13:21:09 -08:00
Philip Rideout
a33c64c736 Update version doc and package per code review. 2018-11-06 12:48:56 -08:00
Philip Rideout
7ea04957d2 Introduce versioning scheme and npm package definition. 2018-11-06 12:48:56 -08:00
Mathias Agopian
8c6cfdba67 Fix erroneous assert 2018-11-06 11:01:08 -08:00
Mathias Agopian
e5cfbeff4d Attempt to fix windows build
Align atomic HeadPtr to 64-bits.
2018-11-05 19:37:31 -08:00
Mathias Agopian
68c6afa72e Fix reuse after free and API inconsistency in job system (#446)
* Fix a reuse after free in the job system

Jobs were destroyed and recycled while still in use
by wait() or run(). To fix this we introduce reference-counting of
jobs.

Jobs start with a ref-count of 1, which is decremented when a job
naturally finishes. Additionally, all user-facing methods acquire
a reference for the duration of the call.

* Fix an API inconsistency with JobSystem

JobSystem's API lets the user create jobs but not destroy them.
Jobs are destroyed automatically, without a way for the caller to
know when that happens.

We now explicitly enforce that jobs are no longer valid when
wait() returns. Multiple concurrent wait() are allowed however.

This is enforced by clearing the job pointer upon returning
from JobSystem::wait(Job* job).

* Rename linked-list put/get to push/pop

* Better fix for Job use after free

There was still a race condition where a run()'ed
job could be destroyed before wait() was called,
wait would then use a destroyed object.

The available APIs now are:

run() - runs and destroys a job
runAndWait() - run, then waits for and destroys a job
runAndRetain() - runs and keep a reference to the job
wait() - waits and destroys a job

wait() can only be used with a job obtained with runAndRetain().

* Get rid of unused code

This version of parallel_for has use-after-free issues anyways,
since we changed the semantics of run/wait/etc...

* Fix decRef() memory order

decRef() must ensure that all access to the 
object have happened before destroying it.

* Fix memory order in atomic linked list's pop()

It needs acquire semantic, since we want to make
sure that no read/write are reordered before the
pop() -- which returns an object to the caller.

* Fix memory order on runningJobCount

we needed acquire semantic when about to destroy
the last job -- it's similar to decRef.

* Comment usages of std::memory_order_*

* Fix AtomicFreeList A-B-A bug

Turns out AtomicFreeList was not immune to the ABA bug. W're fixing
it here by using a 64-bits CAS, which is available on aarch64 and armv7.
2018-11-05 19:12:13 -08:00
Romain Guy
bb3e9a47e5 Update link to CIE colormetric tables
Fixes #452
2018-11-05 15:53:41 -08:00
Philip Rideout
8d410af4b3 JS API: prepare for compressed textures.
Note that we need to allow clients to find out if compressed textures
are supported BEFORE the engine is created, so that they can start
downloading the correct set of texture files. To allow this, we now
expose a getSupportedFormats function that creates a throw-away canvas
in order to query extensions (creating a temporary canvas is a technique
that we also used in the old PNG decoder that we removed in 7b36ed4)
2018-11-03 13:07:02 -07:00
Romain Guy
61112c02c1 Fix typo 2018-11-02 17:23:14 -07:00
Philip Rideout
816fb99b2f Repair WebGL, which does not support MapBufferRange. 2018-11-01 21:55:10 -07:00
Philip Rideout
9de7ffaf84 Fix typo in title tag for suzanne. 2018-11-01 17:11:58 -07:00
Philip Rideout
e3e16fe28b Remove defunct web/tests folder.
These are broken and no longer necessary now that we have web/samples.

Despite the name, these weren't real tests; they were ignored by CMake
and did not run in an automated manner. Going forward we need a better
solution for automated testing.
2018-11-01 17:05:30 -07:00
Philip Rideout
bb0c21b8ea Rename filamentjs to filament-js to follow convention. 2018-11-01 17:05:30 -07:00
Philip Rideout
c46ecc6e05 Port the Suzanne demo to JavaScript. 2018-11-01 17:05:30 -07:00
Philip Rideout
fffb2c8c97 Add samples to the new web directory. 2018-11-01 17:05:30 -07:00
Philip Rideout
a42dcd2291 JavaScript code re-org (#443)
This removes the old CPP-based web samples in favor of the new JS API.

The `web/samples` folder will be added in a subsequent commit.
2018-11-01 17:05:30 -07:00
Romain Guy
4e3a88bbab Cleanup and documentation (#445) 2018-11-01 16:36:52 -07:00
Mathias Agopian
02d45d464c Never use QCOM_tiled_rendering
It turns out that it degrades performance even in cases where we
thought it would help. It seems to always trigger a "glFlush"
and has a very large CPU overhead. It looks like
glInvalidateFramebuffer() does a better job.
2018-11-01 11:30:38 -07:00
Mathias Agopian
c758b53f00 Factor out the UBO uploading code
This is just so that in the future we could reuse that
code for other buffers as it's not specific to UBOs.
2018-11-01 11:30:38 -07:00
Mathias Agopian
4e3c2582e0 Rename loadFoo() to updateFoo() in the driverAPI
Now all buffer updating methods are called updateXXX().
2018-11-01 11:30:38 -07:00
Mathias Agopian
cc1ba4fac7 Reorder methods in OpenGLDriver to match declaration
This change just reorder the load/update buffer/textures
methods so they match the declaration in DriverAPI.inc,
and are grouped together.
2018-11-01 11:30:38 -07:00
Mathias Agopian
f52087ec1e Attempt to upload streaming ubo data more efficiently
IF an UBO is marked as STREAM, meaning that data will
change every frame and the buffer content is invalidated
as well, then we use map/unmap buffer in asynchronous
mode to copy the data into the buffer.

This assumes the buffer is sized to be significantly
larger than the average amount of data used every
frame.
2018-11-01 11:30:38 -07:00
Mathias Agopian
2ac04c27d8 Add support for ANDROID_get_frame_timestamps
We don't make use of it yet, though.
2018-11-01 11:30:38 -07:00
Mathias Agopian
211eddfbe0 Add a couple missing DEBUG_MARKER() calls
Also, store a couple useful glGet*()
2018-11-01 11:30:38 -07:00
Philip Rideout
522a8bedb8 Update filamentjs test runner. 2018-10-30 16:50:24 -07:00
Philip Rideout
8ad655766b JavaScript: concise setBuffer API 2018-10-29 18:09:45 -07:00
Philip Rideout
5da454f652 JavaScript: concise texture creation functions
This makes our createTexture* helpers more consistent with the
createMaterial helper, and extends the wrapped Engine class with new
methods.

Before, clients did something like this:

    const ao = Filament.createTextureFromPng(Filament.assets['foo.png'], engine);
    ...
    engine.destroyTexture(ao);

Now, they can do:

    const ao = engine.createTextureFromPng('foo.png');
    ...
    engine.destroyTexture(ao);

This is more symmetric and concise. In general the JS API now supports a
class of "buffer-like" objects which exploit dynamic typing in order to
accept any of the following: BufferDescriptor, asset string, or
Uint8Array.
2018-10-29 18:09:45 -07:00
Philip Rideout
0deafea0da Add requirements.txt for Python scripts. 2018-10-29 17:53:09 -07:00
Philip Rideout
69e379cf90 Test for JS leaks using emcc --memoryprofiler. 2018-10-29 17:53:09 -07:00
Philip Rideout
e8fddba494 New JS test for memory leaks.
This test recreates a renderable every few frames and dumps the heap
pointer to the console so we can check if it increases over time.

Also simplified the personal web server script so that it uses twisted
and avoid doing a chdir, seems to be more reliable this way.
2018-10-29 17:53:09 -07:00
Romain Guy
31b9801f17 Move textures to nodpi to avoid upscales (#437) 2018-10-29 17:40:34 -07:00
Philip Rideout
4860ff816f JavaScript PNG utility now has SRGB flag. 2018-10-29 17:38:02 -07:00
Philip Rideout
82e61ddc88 Fix the JS IcoSphere tangents.
We were not normalizing one of the orthonormal basis vectors, causing
the visual aberration reported in #423.

This CL also add a "back light" to the redball demo to lighten up the
dark area in the IBL.

Fixes #423
2018-10-29 17:37:42 -07:00
Romain Guy
3a9f3a1984 Update to Kotlin 1.3 (#436) 2018-10-29 17:37:21 -07:00
Philip Rideout
3b793a7994 Fix bad memory access in JS wood demo. 2018-10-26 16:29:26 -07:00
Ben Doherty
e2777b9100 Update libs/filamentjs/tests/test_parquet.js
Co-Authored-By: prideout <philiprideout@gmail.com>
2018-10-26 10:30:44 -07:00
Ben Doherty
96cb5d5951 Update libs/filamentjs/tests/test_parquet.js
Co-Authored-By: prideout <philiprideout@gmail.com>
2018-10-26 10:30:44 -07:00
Ben Doherty
e6bb8a6235 Update libs/filamentjs/tests/test_parquet.js
Co-Authored-By: prideout <philiprideout@gmail.com>
2018-10-26 10:30:44 -07:00
Ben Doherty
934b75e68b Update libs/filamentjs/tests/test_parquet.js
Co-Authored-By: prideout <philiprideout@gmail.com>
2018-10-26 10:30:44 -07:00
Philip Rideout
58f36d1a7f New JS test: draw shaderball with parquet wood texture.
Shameless recreation of the Android `textured-android` sample. I will
make this into a tutorial soon.

Also made some improvements to the test-builder Python script. Really,
this script exists only to make iteration easier; we need a better
solution for automated tests...
2018-10-26 10:30:44 -07:00
Philip Rideout
83e4402e90 JS: fix use-before-free for PNG data. 2018-10-26 10:05:18 -07:00
Philip Rideout
c6a1900beb Add JS utility for PNG files. 2018-10-26 10:05:18 -07:00
Ben Doherty
17021a1167 Retry installing llvm on Kokoro Windows if failure (#400) 2018-10-25 13:31:43 -07:00
Philip Rideout
f0fbdaec28 JS users no longer need to call loadMathExtensions() 2018-10-25 08:16:32 -07:00
Philip Rideout
b73f58e6ef Expose MeshIO to JavaScript clients. 2018-10-25 08:12:53 -07:00
Mathias Agopian
254e3db215 More per-scene renderable UBO to View... for now
If a scene was used in several views, we would update the renderable
ubo several times per frame, which is not desirable. So we now have
such a UBO per-view. This could change in the future, depending
how we want to manage it.
2018-10-24 15:11:54 -07:00
Mathias Agopian
d9df864de4 Get rid of RangeSet. 2018-10-24 15:11:54 -07:00
Mathias Agopian
995b8361b8 Don't use RangeSet anymore.
It was used only in one place, always with a single range. For now,
we just always upload the whole froxel record buffer (64K).
2018-10-24 15:11:54 -07:00
Mathias Agopian
a61af02038 Pass View& instead of View* around 2018-10-24 15:11:54 -07:00
Mathias Agopian
84bfbac5fa The lights UBO must be per-View
We used to have a UBO for lights per scene, however it's better to 
have one per view, since it depends on the camera heavily and is 
limited in size.
Not doing so, could force us to upload the same UBO multiple times
per frame, for e.g. if the same scene was used in multiple views.
2018-10-24 15:11:54 -07:00
Mathias Agopian
912b301790 Try to bring some sanity to our UBO structures
- move UBO declarations in UibGenerator, i.e. next
  to their Uniform Interface Block definition.
  This should help forgetting to update one but
  not the other.

- move UniformInterfaceBlock.h and SamplerInferfaceBlock.h under
  private/filament/.

- do the same things for samplers
2018-10-24 15:11:54 -07:00
Mathias Agopian
b707027401 Avoid a light UBO data copy
We use the command stream to store the lights
ubo data (16K max), which avoid a copy and simplifies
the implementation. we can get rid of GpuLightBuffer.
2018-10-24 15:11:54 -07:00
Philip Rideout
68dfb5ae80 Introduce reference page for JS.
The docgen script now looks for /// comments in the binding definitions,
and generates markdown from those. The reference page has three
sections: classes, free functions, and enums.
2018-10-24 10:05:08 -07:00
Philip Rideout
ab3770d281 Fix JS docgen script for Kokoro. 2018-10-24 09:33:44 -07:00
Philip Rideout
28c56eb354 build.sh: the web archive now includes JS docs
Before this CL, the web archive had two folders: "public" (which
contained web demos written in C++) and "filamentjs" (for the barebones
WASM).

After this CL, the web archive will instead have "dist" and "docs". The
dist folder will have the WASM and accompanying JS file, while the docs
folder has the tutorials and their accompanying demos.

If your machine has Python 3 installed, then "build -ap webgl" will
attempt to generate the JavaScript docs. If Python 3 isn't available,
this build step will be skipped. Note that our Kokoro macOS machines
already have Python 3 pre-installed.

Also removed the pipenv file to simplify the Kokoro Python environment.

Fixes #394.
2018-10-24 08:55:14 -07:00
Ben Doherty
30eccc78b6 Add support for iOS simulator (#398)
* Add support for iOS simulator

* Use correct case for include
2018-10-23 10:30:01 -07:00
Philip Rideout
ab24665396 Allow JS clients to configure FXAA and WebGL context. 2018-10-23 07:58:16 -07:00
Philip Rideout
9e7b589d0a Fix FXAA pixel alignment.
Fixes #397.
2018-10-19 16:42:01 -07:00
Ben Doherty
979b6f00fd Default to Java 9 on Windows Kokoro (#413) 2018-10-19 15:17:16 -07:00
Ben Doherty
0754edbf9b Fix broken iOS build (#411) 2018-10-19 12:51:53 -07:00
Philip Rideout
9916af3ab1 Simplify docgen script per code review. 2018-10-19 12:40:38 -07:00
Philip Rideout
dcfbe694b1 Add fullscreen button to embedded JS demos. 2018-10-19 12:40:38 -07:00
Philip Rideout
835cc93b71 Doc generator now uses argparse. 2018-10-19 12:40:38 -07:00
Mathias Agopian
2f14ac64a7 fix iOS build on case-sensitive filesystems 2018-10-19 11:42:54 -07:00
Mathias Agopian
e96f982c9f Fix typo in RangeSet
Fixes #407
2018-10-19 11:42:39 -07:00
Philip Rideout
605a836565 Add JavaScript utility functions to reduce boilerplate. (#406)
* JS utilities: simplified render method.

* redball tutorial now rotates camera.

* JS utilities: add createTextureFromKtx.

* JS utilities: add createIblFromKtx.

* Repair testwasm.

* Update libs/filamentjs/docs/tutorial_redball.md

Co-Authored-By: prideout <philiprideout@gmail.com>

* Update libs/filamentjs/docs/tutorial_redball.md

Co-Authored-By: prideout <philiprideout@gmail.com>

* Update libs/filamentjs/docs/tutorial_redball.md

Co-Authored-By: prideout <philiprideout@gmail.com>
2018-10-18 18:19:05 -07:00
Mathias Agopian
1cde20d822 Avoid a normalize() in the vertex shader
This is achieved by pre-scaling the normals 
transform so that the resulting normal doesn't have
any large component allowing to do the normalize()
in the fragment shader in mediump.

This must be done for skinning too.
2018-10-18 17:37:06 -07:00
Mathias Agopian
a547f11a8c fix more comment typos 2018-10-18 16:49:47 -07:00
Philip Rideout
83de0fc3e8 The redball tutorial now describes matc and cmgen.
This commit also adds a help option to `build.py` and allows
customization of the output folder.
2018-10-18 16:46:15 -07:00
Ben Doherty
3ed2dd08fb Handle device orientation changes (#399) 2018-10-18 14:43:32 -07:00
Ben Doherty
db729b157a Remove development team for iOS sample (#401) 2018-10-18 13:53:55 -07:00
Ben Doherty
29119c7248 Add iOS support to Filament (#360) 2018-10-18 12:42:13 -07:00
Philip Rideout
356c3a2c24 Flesh out the JavaScript tutorials. 2018-10-18 12:29:22 -07:00
Philip Rideout
b0853b5eb6 jsbindings: fix buffer lifetime bug.
This is a fix-up to f5319f2.
2018-10-18 12:29:22 -07:00
Philip Rideout
d324dc724a Start another literate tutorial / demo for JavaScript.
Unlike the triangle tutorial, this is more focused on materials and 3D
rendering. While much of the verbiage is still TBD, the code snippets
already produce a functional demo after being glued together by the
tangler script.
2018-10-18 12:29:22 -07:00
Romain Guy
e6435a7431 Update README 2018-10-17 16:30:30 -07:00
Philip Rideout
d3e7f40398 Add IBL to jsbinding and testwasm. 2018-10-17 16:13:53 -07:00
Philip Rideout
f5319f2492 jsbindings: fix buffer lifetime bug.
In order to match its JavaScript counterpart, the Buffer wrapper needs
to use reference counting, and the easiest way to achieve that is
with shared_ptr.
2018-10-17 16:13:53 -07:00
Philip Rideout
e00ba80561 jsbindings: add JS utilities, enhance testwasm. 2018-10-17 10:17:04 -07:00
Philip Rideout
d15e275de0 Add glMatrix to third_party.
Some of our web tests and demos will make use of this library, and it's
useful to leverage a local copy instead of a CDN during development.
2018-10-17 09:57:08 -07:00
Romain Guy
920cc7a84a Normalize normals and tangents in the vertex shader (#388)
This avoids breaking bitangents when a scale value is set in the
transform matrix
2018-10-16 17:12:54 -07:00
Mathias Agopian
3b504f2941 add Stream::getTimestamp() to java api 2018-10-16 16:52:26 -07:00
Mathias Agopian
f697b41a2b fix a few typos in comments 2018-10-16 16:50:10 -07:00
Mathias Agopian
8452c4f8f2 Add usage hints to the driver 2018-10-16 16:23:24 -07:00
Mathias Agopian
c238027e49 implemented Stream::getTimestamp()
this returns a timestamp in nanosecond corresponding
to the desired presentation time of the latest frame
bound to a filament texture.
2018-10-16 15:56:43 -07:00
Mathias Agopian
b374340ebb skinning allows 256 bones again, but disallow skews. 2018-10-16 15:56:30 -07:00
Romain Guy
c581b755fd Add helper to upload Android bitmaps to textures (#382)
* WIP Add helper to upload Android bitmaps to textures

* Add missing file

* Pass custom constants for bitmap formats

* Add sample app for texturing

* Update the README for samples

* Rename variable from callback to autoBitmap
2018-10-16 14:24:18 -07:00
Philip Rideout
9e5f44bd34 Consolidate two filamesh readers into a lib. 2018-10-16 13:31:16 -07:00
Philip Rideout
d9204052c8 Fix potential issue with detached ArrayBuffer. 2018-10-15 18:35:53 -07:00
Philip Rideout
5a8d4d818c Remove bad exception handler from wasmloader.
It is incorrect to assume that any exception thrown during onReady
must be due to a faulty download.
2018-10-15 18:35:53 -07:00
Philip Rideout
cd84980c84 Add tests for filamentjs. 2018-10-15 18:35:53 -07:00
Philip Rideout
9b8d5fdf12 Add JS bindings for Texture, PixelBufferDescriptor, et al. 2018-10-15 18:35:53 -07:00
Romain Guy
7cb01929a3 Clean up Kotlin samples (#380)
* Clean up Kotlin samples

* Further code cleanup
2018-10-15 15:28:39 -07:00
Philip Rideout
e7777ef330 Add literate JavaScript tutorial and Python tangler.
This commit contains a markdown file for a "hello triangle" tutorial
as well as a Python script that does two things:

1) Extracts JavaScript code blocks from the markdown to generate the
   code for the demo.

2) Converts the markdown into a nicely-rendered web page.

After we invoke the Python script for the first time, it will create
a set of files in the root docs folder, which will automatically be
published via GitHub Pages to the following URL.

    https://google.github.io/filament/webgl

This gives us an official place to host the latest web demos and
tutorials.

The tutorial can be previewed
[here](https://prideout.net/prototype/webgl/tutorial_triangle.html).
More JavaScript tutorials are forthcoming.
2018-10-15 12:42:13 -07:00
Philip Rideout
0326d60d0a jsbindings: simplify BufferDescriptor wrapper.
The JS BufferDescriptor wrapper is used in two directions: JS => WASM
and WASM => JS. In both cases, it needs to be sure to delete the
underlying driver::BufferDescriptor. This is safe because the contents
of the underlying descriptor are always moved to its final destination
before the wrapper is destroyed.
2018-10-15 12:16:02 -07:00
Ben Doherty
0d709b6074 Add a few new nodes to Tungsten (#326) 2018-10-15 12:02:05 -07:00
Ben Doherty
6992c8a584 Update README on linking against Filament (#379)
* Add section to README on linking against Filament
2018-10-15 11:18:45 -07:00
Philip Rideout
7c54faa8bd Add filamentjs to web release archive.
The web archive now has two subfolders: public and filamentjs.

- public contains the three C++ based web demos.
- jsbindings contains the new JS bindings and Filament-only WASM module.
2018-10-15 10:43:28 -07:00
Romain Guy
3a0f9791cc Remove outdated comment 2018-10-12 18:37:34 -07:00
Philip Rideout
473824b15e Introduce JavaScript API using embind.
This adds filamentjs to libs, which builds a Filament-only WASM file.
Unlike our existing web demos, this WASM file contains no application
code.

These bindings are by no means complete, but they are sufficient for a
"hello triangle" demo that I have working locally. Sneak peak of the
Triangle JS app code:

    https://gist.github.com/prideout/e9d6dd0e312146c8a287d2d109affa4d

This commit merely adds the bindings to the build. Demos and docs are
forthcoming.
2018-10-12 17:09:52 -07:00
Mathias Agopian
b4c77166b7 fix normals when skinning with non-uniform scales 2018-10-11 18:46:42 -07:00
Mathias Agopian
920113b076 fix a typo that caused skinning to just not work 2018-10-11 16:50:52 -07:00
Philip Rideout
2a10f8122d Remove STB from web samples.
Every texture now uses KTX, so there is no need to fatten the WASM with
a PNG decoder.
2018-10-11 11:34:05 -07:00
Philip Rideout
182d95c6ce Use filamat file instead of inc in a web sample.
For test coverage, ensure that one of our C++ based web samples
uses a filamat file instead of inc, because this is how the JS based
web samples will work.

This makes pre-zipped triangle wasm go from 747 KB to 741 KB.
2018-10-11 10:22:48 -07:00
Philip Rideout
ed76aa04ca Shrink WASM files 30% by excluding VK materials. 2018-10-11 09:43:21 -07:00
Jonas Karlsson
76035711f4 Fix documentation typo (#368) 2018-10-11 08:53:51 -07:00
Philip Rideout
7959f31ebf OpenGL Backend fix for INVALID_OPERATION regression.
Recent changes to UBO management introduce the possibility of calling
glBufferSubData on zero-length regions, which cause a cascade of
GL_INVALID_OPERATION with the Suzanne WebGL demo. Note that the WebGL
variant of BufferSubData has special behavior when length = 0.

This also seems like a potentially useful optimization for non-web
platforms, since a CPU branch is likely cheaper than a GL entry point.

This commit also seems to fix a segfault seen with the "vk_imgui" sample
after changing its backend to OPENGL.

Fixes #366
2018-10-10 14:01:13 -07:00
Philip Rideout
10a3addf1a Filaweb now uses compression for the IBL.
The web build now invokes cmgen three times for each IBL: uncompressed,
S3TC (desktop), and ETC (mobile). Alternatively, we could enhance cmgen
to accept multiple compression strings, but per discussion with Mathias
we decided to keep it simple.

I decided not to use compression for the skybox in our web demos because
the quality is too poor (see PR image). I did not try playing with
compression settings, might be worth revisiting in the future.
2018-10-10 12:09:25 -07:00
Romain Guy
89982e6e97 Improve APIs for Kotlin usage (#361) 2018-10-10 10:13:07 -07:00
265 changed files with 11079 additions and 3545 deletions

View File

@@ -270,6 +270,7 @@ add_subdirectory(${EXTERNAL}/libgtest/tnt)
add_subdirectory(${LIBRARIES}/filabridge)
add_subdirectory(${LIBRARIES}/filaflat)
add_subdirectory(${LIBRARIES}/filamat)
add_subdirectory(${LIBRARIES}/filameshio)
add_subdirectory(${LIBRARIES}/image)
add_subdirectory(${LIBRARIES}/math)
add_subdirectory(${LIBRARIES}/utils)
@@ -289,7 +290,8 @@ endif()
set (FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
if (WEBGL)
add_subdirectory(${FILAMENT}/samples/web)
add_subdirectory(web/filament-js)
add_subdirectory(web/samples)
add_subdirectory(${LIBRARIES}/filagui)
@@ -297,7 +299,7 @@ if (WEBGL)
add_subdirectory(${EXTERNAL}/stb/tnt)
endif()
if (NOT ANDROID AND NOT WEBGL)
if (NOT ANDROID AND NOT WEBGL AND NOT IOS)
add_subdirectory(${FILAMENT}/samples)
add_subdirectory(${LIBRARIES}/bluegl)
@@ -334,7 +336,7 @@ if (NOT ANDROID AND NOT WEBGL)
add_subdirectory(${TOOLS}/specular-color)
endif()
# Generate exported executables for cross-compiled builds (Android and WebGL)
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
if (NOT CMAKE_CROSSCOMPILING)
export(TARGETS matc cmgen filamesh mipgen FILE ${IMPORT_EXECUTABLES})
endif()

View File

@@ -1,13 +1,14 @@
# Filament
<img alt="Android" src="build/img/android.png" width="20px" height="20px" hspace="2px"/>[![Android Build Status](https://filament-build.storage.googleapis.com/badges/build_status_android.svg)](https://filament-build.storage.googleapis.com/badges/build_link_android.html)
<img alt="iOS" src="build/img/macos.png" width="20px" height="20px" hspace="2px"/>[![iOS Build Status](https://filament-build.storage.googleapis.com/badges/build_status_ios.svg)](https://filament-build.storage.googleapis.com/badges/build_link_ios.html)
<img alt="Linux" src="build/img/linux.png" width="20px" height="20px" hspace="2px"/>[![Linux Build Status](https://filament-build.storage.googleapis.com/badges/build_status_linux.svg)](https://filament-build.storage.googleapis.com/badges/build_link_linux.html)
<img alt="macOS" src="build/img/macos.png" width="20px" height="20px" hspace="2px"/>[![MacOS Build Status](https://filament-build.storage.googleapis.com/badges/build_status_mac.svg)](https://filament-build.storage.googleapis.com/badges/build_link_mac.html)
<img alt="Windows" src="build/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://filament-build.storage.googleapis.com/badges/build_status_windows.svg)](https://filament-build.storage.googleapis.com/badges/build_link_windows.html)
<img alt="Web" src="build/img/web.png" width="20px" height="20px" hspace="2px"/>[![Web Build Status](https://filament-build.storage.googleapis.com/badges/build_status_web.svg)](https://filament-build.storage.googleapis.com/badges/build_link_web.html)
Filament is a real-time physically based rendering engine for Android, Linux, macOS, Windows, and
WebGL. It is designed to be as small as possible and as efficient as possible on Android.
Filament is a real-time physically based rendering engine for Android, iOS, Linux, macOS, Windows,
and WebGL. It is designed to be as small as possible and as efficient as possible on Android.
Filament is currently used in the
[Sceneform](https://developers.google.com/ar/develop/java/sceneform/) library both at runtime on
@@ -53,13 +54,14 @@ Here are a few sample materials rendered with Filament:
- Native C++ API for Android, Linux, macOS and Windows
- Java/JNI API for Android, Linux, macOS and Windows
- [Python bindings](https://github.com/artometa/pyfilament)
- JavaScript API
### Backends
- OpenGL 4.1+ for Linux, macOS and Windows
- OpenGL ES 3.0+ for Android
- Vulkan 1.0 for Android, Linux, macOS (with MoltenVk) and Windows
- OpenGL ES 3.0+ for Android and iOS
- Vulkan 1.0 for Android, Linux, macOS and iOS (with MoltenVk), and Windows
- WebGL 2.0 for all platforms
### Rendering
@@ -108,6 +110,7 @@ Many other features have been either prototyped or planned:
- `math`: Mathematica notebooks used to explore BRDFs, equations, etc.
- `filament`: Filament engine
- `ide`: Configuration files for IDEs (CLion, etc.)
- `ios`: Sample projects for iOS
- `java`: Java bindings for Filament libraries
- `libs`: Libraries
- `bluegl`: OpenGL bindings for macOS, Linux and Windows
@@ -116,6 +119,7 @@ Many other features have been either prototyped or planned:
- `filaflat`: Serialization/deserialization library used for materials
- `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui)
- `filamat`: Material generation library
- `filameshio`: Tiny mesh parsing library (see also `tools/filamesh`)
- `image`: Image filtering and simple transforms
- `imageio`: Image file reading / writing, only intended for internal use
- `math`: Math library
@@ -132,7 +136,7 @@ Many other features have been either prototyped or planned:
- `matinfo` Displays information about materials compiled with `matc`
- `mipgen` Generates a series of miplevels from a source image.
- `normal-blending`: Tool to blend normal maps
- `roughness-prefilter`: Pre-filters a roughness map from a normal map to reduce aliasing
- `roughness-prefilter`: Pre-filters a roughness map from a normal map to reduce aliasing
- `skygen`: Physically-based sky environment texture generator
- `specular-color`: Computes the specular color of conductors based on spectral data
@@ -299,6 +303,17 @@ $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/fi
$ ninja
```
### iOS
The easiest way to build Filament for iOS is to use `build.sh` and the
`-p ios` flag. For instance to build the debug target:
```
$ ./build.sh -p ios debug
```
See [ios/samples/README.md](./ios/samples/README.md) for more information.
### Windows
The following instructions have been tested on a machine running Windows 10. They should take you
@@ -700,18 +715,24 @@ export EMSDK=<your chosen home for the emscripten SDK>
```
The EMSDK variable is required so that the build script can find the Emscripten SDK. The build
creates a `public` folder that can be used as the root of a simple static web server. Note that you
creates a `samples` folder that can be used as the root of a simple static web server. Note that you
cannot open the HTML directly from the filesystem due to CORS. One way to deal with this is to
use Python to create a quick localhost server:
```
cd out/cmake-web-release/samples/web/public
cd out/cmake-webgl-release/web/samples
python3 -m http.server # Python 3
python -m SimpleHTTPServer # Python 2.7
```
Each sample app has its own handwritten html file, wasm file, and js loader. Additionally the public
folder contains meshes, textures, and the tiny `filaweb.js` library.
You can then open http://localhost:8000/suzanne.html in your web browser.
Alternatively, if you have node installed you can use the
[live-server](https://www.npmjs.com/package/live-server) package, which automatically refreshes the
web page when it detects a change.
Each sample app has its own handwritten html file. Additionally the server folder contains assets
such as meshes, textures, and materials.
## Running the native samples
@@ -730,7 +751,7 @@ in your build directory). These sample apps expect a path to a directory contain
for the IBL. To generate an IBL simply use this command:
```
cmgen -x ./ibls/ my_ibl.exr
cmgen -x ./ibls/ my_ibl.exr
```
The source environment map can be a PNG (8 or 16 bit), a PSD (16 or 32 bit), an HDR or an OpenEXR
@@ -840,6 +861,14 @@ package `com.google.android.filament.android`. All you need to do is set a rende
helper and attach your `SurfaceView` or `TextureView` to it. You are still responsible for
creating the swap chain in the `onNativeWindowChanged()` callback.
### iOS
See `ios/samples` for examples of using Filament on iOS.
Filament on iOS is largely the same as native rendering with C++. A `CAEAGLLayer` or `CAMetalLayer`
is passed to the `createSwapChain` method. Filament for iOS supports both OpenGL ES and Vulkan via
MoltenVK.
## Generating C++ documentation
To generate the documentation you must first install `doxygen`, then run the following commands:
@@ -870,7 +899,7 @@ Host tools (such as `matc` or `cmgen`) can use external dependencies freely.
## How to make contributions
Please read and follow the steps in [CONTRIBUTING.md](/CONTRIBUTING.md). Make sure you are
familiar with the [code style](/CODE_STYLE.md).
familiar with the [code style](/CODE_STYLE.md).
## License

View File

@@ -73,6 +73,7 @@ target_link_libraries(filament-jni
GLESv3
EGL
android
jnigraphics
)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)

View File

@@ -13,7 +13,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.android.tools.build:gradle:3.2.1'
}
}

View File

@@ -56,7 +56,8 @@ JniCallback::~JniCallback() {
void JniCallback::invoke(void*, size_t, void* user) {
JniCallback* data = reinterpret_cast<JniCallback*>(user);
delete data;
// don't call delete here, because we don't own the storage
data->~JniCallback();
}
JniBufferCallback* JniBufferCallback::make(filament::Engine* engine,

View File

@@ -24,8 +24,8 @@ using namespace utils;
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nSetSkybox(JNIEnv *env, jclass type, jlong nativeScene,
jlong nativeSkybox) {
Scene *scene = (Scene *) nativeScene;
Skybox *skybox = (Skybox *) nativeSkybox;
Scene* scene = (Scene*) nativeScene;
Skybox* skybox = (Skybox*) nativeSkybox;
scene->setSkybox(skybox);
}
@@ -33,33 +33,33 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nSetIndirectLight(JNIEnv *env, jclass type,
jlong nativeScene, jlong nativeIndirectLight) {
Scene *scene = (Scene *) nativeScene;
IndirectLight *indirectLight = (IndirectLight *) nativeIndirectLight;
IndirectLight* indirectLight = (IndirectLight*) nativeIndirectLight;
scene->setIndirectLight(indirectLight);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nAddEntity(JNIEnv *env, jclass type, jlong nativeScene,
jint entity) {
Scene *scene = (Scene *) nativeScene;
scene->addEntity((Entity &) entity);
Scene* scene = (Scene*) nativeScene;
scene->addEntity((Entity&) entity);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Scene_nRemove(JNIEnv *env, jclass type, jlong nativeScene,
jint entity) {
Scene *scene = (Scene *) nativeScene;
scene->remove((Entity &) entity);
Scene* scene = (Scene*) nativeScene;
scene->remove((Entity&) entity);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Scene_nGetRenderableCount(JNIEnv *env, jclass type,
jlong nativeScene) {
Scene *scene = (Scene *) nativeScene;
Scene* scene = (Scene*) nativeScene;
return (jint) scene->getRenderableCount();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Scene_nGetLightCount(JNIEnv *env, jclass type, jlong nativeScene) {
Scene *scene = (Scene *) nativeScene;
Scene* scene = (Scene*) nativeScene;
return (jint) scene->getLightCount();
}

View File

@@ -154,3 +154,9 @@ Java_com_google_android_filament_Stream_nReadPixels(JNIEnv *env, jclass,
return 0;
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Stream_nGetTimestamp(JNIEnv*, jclass, jlong nativeStream) {
Stream *stream = (Stream *) nativeStream;
return stream->getTimestamp();
}

View File

@@ -19,6 +19,10 @@
#include <algorithm>
#include <functional>
#ifdef ANDROID
#include <android/bitmap.h>
#endif
#include <filament/driver/BufferDescriptor.h>
#include <filament/Engine.h>
#include <filament/Stream.h>
@@ -37,7 +41,7 @@ static size_t getTextureDataSize(const Texture *texture, size_t level,
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv*, jclass,
jlong nativeEngine, jint internalFormat) {
Engine *engine = (Engine *) nativeEngine;
return (jboolean) Texture::isTextureFormatSupported(*engine,
@@ -47,68 +51,68 @@ Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv *env,
// Texture::Builder...
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Texture_nCreateBuilder(JNIEnv *env, jclass type) {
Java_com_google_android_filament_Texture_nCreateBuilder(JNIEnv*, jclass) {
return (jlong) new Texture::Builder();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nDestroyBuilder(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nDestroyBuilder(JNIEnv*, jclass,
jlong nativeBuilder) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
delete builder;
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderWidth(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderWidth(JNIEnv*, jclass,
jlong nativeBuilder, jint width) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->width((uint32_t) width);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderHeight(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderHeight(JNIEnv*, jclass,
jlong nativeBuilder, jint height) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->height((uint32_t) height);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderDepth(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderDepth(JNIEnv*, jclass,
jlong nativeBuilder, jint depth) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->depth((uint32_t) depth);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderLevels(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderLevels(JNIEnv*, jclass,
jlong nativeBuilder, jint levels) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->levels((uint8_t) levels);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderSampler(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderSampler(JNIEnv*, jclass,
jlong nativeBuilder, jint sampler) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->sampler((Texture::Sampler) sampler);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderFormat(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderFormat(JNIEnv*, jclass,
jlong nativeBuilder, jint format) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->format((Texture::InternalFormat) format);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderRgbm(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderRgbm(JNIEnv*, jclass,
jlong nativeBuilder, jboolean enable) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->rgbm(enable);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv*, jclass,
jlong nativeBuilder, jlong nativeEngine) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine;
@@ -118,60 +122,60 @@ Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv *env, jclass type,
// Texture...
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetWidth(JNIEnv *env, jclass type, jlong nativeTexture,
Java_com_google_android_filament_Texture_nGetWidth(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getWidth((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetHeight(JNIEnv *env, jclass type, jlong nativeTexture,
Java_com_google_android_filament_Texture_nGetHeight(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getHeight((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetDepth(JNIEnv *env, jclass type, jlong nativeTexture,
Java_com_google_android_filament_Texture_nGetDepth(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getDepth((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetLevels(JNIEnv *env, jclass type, jlong nativeTexture) {
Java_com_google_android_filament_Texture_nGetLevels(JNIEnv*, jclass, jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getLevels();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetTarget(JNIEnv *env, jclass type, jlong nativeTexture) {
Java_com_google_android_filament_Texture_nGetTarget(JNIEnv*, jclass, jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getTarget();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetInternalFormat(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nGetInternalFormat(JNIEnv*, jclass,
jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getFormat();
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nGetRgbm(JNIEnv *env, jclass type, jlong nativeTexture) {
Java_com_google_android_filament_Texture_nGetRgbm(JNIEnv*, jclass, jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return texture->isRgbm();
return static_cast<jboolean>(texture->isRgbm());
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImage(JNIEnv *env, jclass type_, jlong nativeTexture,
Java_com_google_android_filament_Texture_nSetImage(JNIEnv* env, jclass, jlong nativeTexture,
jlong nativeEngine, jint level, jint xoffset, jint yoffset, jint width, jint height,
jobject storage, jint remaining,
jint left, jint bottom, jint type, jint alignment,
jint stride, jint format,
jobject handler, jobject runnable) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
Texture* texture = (Texture*) nativeTexture;
Engine* engine = (Engine*) nativeEngine;
size_t sizeInBytes = getTextureDataSize(texture, (size_t) level, (Texture::Format) format,
(Texture::Type) type, (size_t) stride, (size_t) alignment);
@@ -196,9 +200,9 @@ Java_com_google_android_filament_Texture_nSetImage(JNIEnv *env, jclass type_, jl
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCompressed(JNIEnv *env, jclass type_, jlong nativeTexture,
jlong nativeEngine, jint level, jint xoffset, jint yoffset, jint width, jint height,
jobject storage, jint remaining,
Java_com_google_android_filament_Texture_nSetImageCompressed(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jint xoffset, jint yoffset,
jint width, jint height, jobject storage, jint remaining,
jint left, jint bottom, jint type, jint alignment,
jint compressedSizeInBytes, jint compressedFormat,
jobject handler, jobject runnable) {
@@ -227,7 +231,7 @@ Java_com_google_android_filament_Texture_nSetImageCompressed(JNIEnv *env, jclass
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass type_,
Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jobject storage, jint remaining,
jint left, jint bottom, jint type, jint alignment, jint stride, jint format,
jintArray faceOffsetsInBytes_,
@@ -262,7 +266,7 @@ Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass ty
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env, jclass type_,
Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jobject storage, jint remaining,
jint left, jint bottom, jint type, jint alignment,
jint compressedSizeInBytes, jint compressedFormat, jintArray faceOffsetsInBytes_,
@@ -297,14 +301,15 @@ Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env,
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlong nativeTexture, jlong nativeEngine, jlong eglImage) {
Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlong nativeTexture,
jlong nativeEngine, jlong eglImage) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
texture->setExternalImage(*engine, (void*)eglImage);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv*, jclass,
jlong nativeTexture, jlong nativeEngine, jlong nativeStream) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
@@ -313,7 +318,7 @@ Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv *env, jclass
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv *env, jclass type,
Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv*, jclass,
jlong nativeTexture, jlong nativeEngine) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
@@ -323,9 +328,113 @@ Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv *env, jclass ty
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsStreamValidForTexture(JNIEnv*, jclass,
jlong nativeTexture, jlong nativeStream) {
jlong nativeTexture, jlong) {
Texture* texture = (Texture*) nativeTexture;
Stream* stream = (Stream*) nativeStream;
return (jboolean) (texture->getTarget() == SamplerType::SAMPLER_EXTERNAL);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// ANDROID SPECIFIC BITS
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef ANDROID
#define BITMAP_CONFIG_ALPHA_8 0
#define BITMAP_CONFIG_RGB_565 1
#define BITMAP_CONFIG_RGBA_4444 2
#define BITMAP_CONFIG_RGBA_8888 3
#define BITMAP_CONFIG_RGBA_F16 4
#define BITMAP_CONFIG_HARDWARE 5
class AutoBitmap {
public:
AutoBitmap(JNIEnv* env, jobject bitmap) noexcept
: mEnv(env)
, mBitmap(env->NewGlobalRef(bitmap))
{
if (mBitmap) {
AndroidBitmap_getInfo(mEnv, mBitmap, &mInfo);
AndroidBitmap_lockPixels(mEnv, mBitmap, &mData);
}
}
~AutoBitmap() noexcept {
if (mBitmap) {
AndroidBitmap_unlockPixels(mEnv, mBitmap);
mEnv->DeleteGlobalRef(mBitmap);
}
}
AutoBitmap(AutoBitmap &&rhs) noexcept {
mEnv = rhs.mEnv;
std::swap(mData, rhs.mData);
std::swap(mBitmap, rhs.mBitmap);
std::swap(mInfo, rhs.mInfo);
}
void* getData() const noexcept {
return mData;
}
size_t getSizeInBytes() const noexcept {
return mInfo.height * mInfo.stride;
}
PixelDataFormat getFormat(int format) const noexcept {
// AndroidBitmapInfo does not capture the HARDWARE and RGBA_F16 formats
// so we switch on the Bitmap.Config values directly
switch (format) {
case BITMAP_CONFIG_ALPHA_8: return PixelDataFormat::ALPHA;
case BITMAP_CONFIG_RGB_565: return PixelDataFormat::RGB;
default: return PixelDataFormat::RGBA;
}
}
PixelDataType getType(int format) const noexcept {
switch (format) {
case BITMAP_CONFIG_RGBA_F16: return PixelDataType::HALF;
default: return PixelDataType::UBYTE;
}
}
static void invoke(void* buffer, size_t n, void* user) {
AutoBitmap* data = reinterpret_cast<AutoBitmap*>(user);
data->~AutoBitmap();
}
static AutoBitmap* make(Engine* engine, JNIEnv* env, jobject bitmap) {
void* that = engine->streamAlloc(sizeof(AutoBitmap), alignof(AutoBitmap));
return new (that) AutoBitmap(env, bitmap);
}
private:
JNIEnv* mEnv;
void* mData = nullptr;
jobject mBitmap = nullptr;
AndroidBitmapInfo mInfo;
};
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_android_TextureHelper_nSetBitmap(JNIEnv* env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jint xoffset, jint yoffset,
jint width, jint height, jobject bitmap, jint format) {
Texture* texture = (Texture*) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
auto* autoBitmap = AutoBitmap::make(engine, env, bitmap);
Texture::PixelBufferDescriptor desc(
autoBitmap->getData(),
autoBitmap->getSizeInBytes(),
autoBitmap->getFormat(format),
autoBitmap->getType(format),
&AutoBitmap::invoke, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
}
#endif

View File

@@ -24,6 +24,8 @@
#import <Cocoa/Cocoa.h>
#pragma clang diagnostic push
#pragma ide diagnostic ignored "NotReleasedValue"
extern "C" {
void *getNativeWindow(JNIEnv *env, jclass klass, jobject surface) {
void *win = nullptr;
@@ -40,7 +42,7 @@ void *getNativeWindow(JNIEnv *env, jclass klass, jobject surface) {
view.wantsLayer = true;
[jawldsip setLayer:view.layer];
win = (void*)view;
win = (void*) view;
releaseDrawingSurface(ds, dsi);
return win;
}
@@ -48,7 +50,7 @@ void *getNativeWindow(JNIEnv *env, jclass klass, jobject surface) {
jlong createNativeSurface(jint width, jint height) {
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width, height)];
view.wantsLayer = true;
return (jlong)view;
return (jlong) view;
}
void destroyNativeSurface(jlong surface) {
@@ -57,3 +59,4 @@ void destroyNativeSurface(jlong surface) {
}
}
#pragma clang diagnostic pop

View File

@@ -25,65 +25,83 @@ static std::vector<int> jawtVersions = {
0x00010009,
};
bool acquireDrawingSurface(JNIEnv *env, jobject surface, JAWT_DrawingSurface** ods,
JAWT_DrawingSurfaceInfo** odsi) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
bool acquireDrawingSurface(JNIEnv* env, jobject surface,
JAWT_DrawingSurface** ods, JAWT_DrawingSurfaceInfo** odsi) {
JAWT awt;
JAWT_DrawingSurface *ds = nullptr;
JAWT_DrawingSurfaceInfo *dsi = nullptr;
JAWT_DrawingSurface* ds = nullptr;
JAWT_DrawingSurfaceInfo* dsi = nullptr;
// Search for a valid AWT
jboolean foundJawt = false;
for(int jawtVersion : jawtVersions) {
jboolean foundJawt = JNI_FALSE;
for (int jawtVersion : jawtVersions) {
awt.version = jawtVersion;
foundJawt = JAWT_GetAWT(env, &awt);
if (foundJawt == JNI_TRUE) {
#ifndef NDEBUG
printf("Found valid AWT v%08x.\n", jawtVersion);
#endif
break;
} else {
#ifndef NDEBUG
printf("AWT v%08x not present.\n", jawtVersion);
#endif
}
}
#ifndef NDEBUG
fflush(stdout);
#endif
if (foundJawt == JNI_FALSE) {
printf("AWT Not found\n");
fflush(stdout);
return 0;
return false;
}
// Get the drawing surface
ds = awt.GetDrawingSurface(env, surface);
if (ds == NULL) {
if (ds == nullptr) {
#ifndef NDEBUG
printf("NULL drawing surface\n");
fflush(stdout);
return 0;
#endif
return false;
}
// Lock the drawing
jint lock = ds->Lock(ds);
if ((lock & JAWT_LOCK_ERROR) != 0) {
#ifndef NDEBUG
printf("Error locking surface\n");
fflush(stdout);
#endif
awt.FreeDrawingSurface(ds);
return 0;
return false;
}
// Get the drawing surface info
dsi = ds->GetDrawingSurfaceInfo(ds);
if (dsi == NULL) {
if (dsi == nullptr) {
#ifndef NDEBUG
printf("Error getting surface info\n");
fflush(stdout);
#endif
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);
return 0;
return false;
}
*odsi = dsi;
*ods = ds;
return 1;
}
void releaseDrawingSurface(JAWT_DrawingSurface *ds, JAWT_DrawingSurfaceInfo *dsi) {
return true;
}
#pragma clang diagnostic pop
void releaseDrawingSurface(JAWT_DrawingSurface* ds, JAWT_DrawingSurfaceInfo* dsi) {
// Free the drawing surface info
ds->FreeDrawingSurfaceInfo(dsi);
// Unlock the drawing surface

View File

@@ -17,7 +17,7 @@
#include <jawt.h>
extern "C" {
bool acquireDrawingSurface(JNIEnv *env, jobject obj, JAWT_DrawingSurface** ds,
JAWT_DrawingSurfaceInfo** dsi);
bool acquireDrawingSurface(JNIEnv* env, jobject surface, JAWT_DrawingSurface** ods,
JAWT_DrawingSurfaceInfo** odsi);
void releaseDrawingSurface(JAWT_DrawingSurface* ds, JAWT_DrawingSurfaceInfo* dsi);
}

View File

@@ -23,17 +23,17 @@
#include<GL/glx.h>
extern "C" {
void *getNativeWindow(JNIEnv *env, jclass, jobject surface) {
void *win = nullptr;
JAWT_DrawingSurface *ds = nullptr;
JAWT_DrawingSurfaceInfo *dsi = nullptr;
void *getNativeWindow(JNIEnv* env, jclass, jobject surface) {
void* win = nullptr;
JAWT_DrawingSurface* ds = nullptr;
JAWT_DrawingSurfaceInfo* dsi = nullptr;
if (!acquireDrawingSurface(env, surface, &ds, &dsi)) {
return win;
}
JAWT_X11DrawingSurfaceInfo *dsi_x11 = (JAWT_X11DrawingSurfaceInfo *) dsi->platformInfo;
JAWT_X11DrawingSurfaceInfo* dsi_x11 = (JAWT_X11DrawingSurfaceInfo*) dsi->platformInfo;
win = (void *) dsi_x11->drawable;
win = (void*) dsi_x11->drawable;
releaseDrawingSurface(ds, dsi);
return win;
}
@@ -77,7 +77,7 @@ jlong createNativeSurface(jint width, jint height) {
XFlush(display);
// Camouflage the pbuffer as a window which are both XID anyway.
return (jlong)window;
return (jlong) window;
}
void destroyNativeSurface(jlong surface) {
@@ -88,4 +88,3 @@ void destroyNativeSurface(jlong surface) {
}
}

View File

@@ -50,18 +50,22 @@ void chooseAndSetPixelFormat(HDC dc) {
SetPixelFormat(dc, pixelFormat, &pfd);
}
void *getNativeWindow(JNIEnv *env, jclass, jobject surface) {
void *win = nullptr;
JAWT_DrawingSurface *ds = nullptr;
JAWT_DrawingSurfaceInfo *dsi = nullptr;
void* getNativeWindow(JNIEnv* env, jclass, jobject surface) {
void* win = nullptr;
JAWT_DrawingSurface* ds = nullptr;
JAWT_DrawingSurfaceInfo* dsi = nullptr;
if (!acquireDrawingSurface(env, surface, &ds, &dsi)) {
return win;
}
JAWT_Win32DrawingSurfaceInfo *dsi_win32 = (JAWT_Win32DrawingSurfaceInfo *) dsi->platformInfo;
JAWT_Win32DrawingSurfaceInfo* dsi_win32 = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo;
HDC dc = dsi_win32->hdc;
chooseAndSetPixelFormat(dsi_win32->hdc);
win = (void *) dsi_win32->hdc;
win = (void*) dsi_win32->hdc;
releaseDrawingSurface(ds, dsi);
return win;
}
@@ -77,13 +81,15 @@ jlong createNativeSurface(jint width, jint height) {
HWND window = CreateWindowA("STATIC", "dummy", 0, 0, 0, width, height, NULL, NULL, NULL, NULL);
SetWindowLong(window, GWL_STYLE, 0); //remove all window styles
HDC dc = GetDC(window);
chooseAndSetPixelFormat(dc);
return (jlong)dc;
return (jlong) dc;
}
void destroyNativeSurface(jlong surface) {
HDC dc = (HDC)surface;
HDC dc = (HDC) surface;
HWND window = WindowFromDC(dc);
ReleaseDC(window, dc);
DestroyWindow(window);

View File

@@ -16,31 +16,53 @@
package com.google.android.filament;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class Scene {
private long mNativeObject;
private @Nullable Skybox mSkybox;
private @Nullable IndirectLight mIndirectLight;
Scene(long nativeScene) {
mNativeObject = nativeScene;
}
public void setSkybox(@NonNull Skybox skybox) {
nSetSkybox(getNativeObject(), skybox.getNativeObject());
@Nullable
public Skybox getSkybox() {
return mSkybox;
}
public void setIndirectLight(@NonNull IndirectLight ibl) {
nSetIndirectLight(getNativeObject(), ibl.getNativeObject());
public void setSkybox(@Nullable Skybox skybox) {
mSkybox = skybox;
nSetSkybox(getNativeObject(), mSkybox != null ? mSkybox.getNativeObject() : 0);
}
@Nullable
public IndirectLight getIndirectLight() {
return mIndirectLight;
}
public void setIndirectLight(@Nullable IndirectLight ibl) {
mIndirectLight = ibl;
nSetIndirectLight(getNativeObject(),
mIndirectLight != null ? mIndirectLight.getNativeObject() : 0);
}
public void addEntity(@Entity int entity) {
nAddEntity(getNativeObject(), entity);
}
public void remove(@Entity int entity) {
public void removeEntity(@Entity int entity) {
nRemove(getNativeObject(), entity);
}
/**
* @deprecated See {@link #removeEntity(int)}
*/
public void remove(@Entity int entity) {
removeEntity(entity);
}
public int getRenderableCount() {
return nGetRenderableCount(getNativeObject());
}

View File

@@ -128,6 +128,10 @@ public class Stream {
}
}
public long getTimestamp() {
return nGetTimestamp(getNativeObject());
}
long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed Stream");
@@ -153,6 +157,7 @@ public class Stream {
Buffer storage, int remaining,
int left, int top, int type, int alignment, int stride, int format,
Object handler, Runnable callback);
private static native long nGetTimestamp(long nativeStream);
private static native boolean nIsNative(long nativeStream);
}

View File

@@ -334,7 +334,7 @@ public class Texture {
}
@NonNull
public Builder rgbm(@NonNull boolean enabled) {
public Builder rgbm(boolean enabled) {
nBuilderRgbm(mNativeBuilder, enabled);
return this;
}
@@ -524,7 +524,8 @@ public class Texture {
int alignment, int compressedSizeInBytes, int compressedFormat,
int[] faceOffsetsInBytes, Object handler, Runnable callback);
private static native void nSetExternalImage(long nativeObject, long nativeObject1, long eglImage);
private static native void nSetExternalImage(
long nativeObject, long nativeEngine, long eglImage);
private static native void nSetExternalStream(long nativeTexture,
long nativeEngine, long nativeStream);

View File

@@ -57,7 +57,13 @@ public class TextureSampler {
int mSampler = 0; // bit field used by native
/**
* Min filter: LINEAR_MIPMAP_LINEAR
* Mag filter: LINEAR
* Wrap mode: REPEAT
*/
public TextureSampler() {
this(MinFilter.LINEAR_MIPMAP_LINEAR, MagFilter.LINEAR, WrapMode.REPEAT);
}
public TextureSampler(@NonNull MagFilter minMag) {

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2018 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.android;
import android.graphics.Bitmap;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import com.google.android.filament.Engine;
import com.google.android.filament.Texture;
import java.lang.reflect.Method;
public final class TextureHelper {
// Keep in sync with Texture.cpp
private static final int BITMAP_CONFIG_ALPHA_8 = 0;
private static final int BITMAP_CONFIG_RGB_565 = 1;
private static final int BITMAP_CONFIG_RGBA_4444 = 2;
private static final int BITMAP_CONFIG_RGBA_8888 = 3;
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() {
}
public static void setBitmap(@NonNull Engine engine,
@NonNull Texture texture, @IntRange(from = 0) int level, @NonNull Bitmap bitmap) {
setBitmap(engine, texture,
level, 0, 0, texture.getWidth(level), texture.getHeight(level), bitmap);
}
public static void setBitmap(@NonNull Engine engine,
@NonNull Texture texture, @IntRange(from = 0) int level,
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@IntRange(from = 0) int width, @IntRange(from = 0) int height,
@NonNull Bitmap bitmap) {
int format = toNativeFormat(bitmap.getConfig());
if (format == BITMAP_CONFIG_RGBA_4444 || format == BITMAP_CONFIG_HARDWARE) {
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
}
}
private static int toNativeFormat(Bitmap.Config config) {
switch (config) {
case ALPHA_8: return BITMAP_CONFIG_ALPHA_8;
case RGB_565: return BITMAP_CONFIG_RGB_565;
case ARGB_4444: return BITMAP_CONFIG_RGBA_4444;
case ARGB_8888: return BITMAP_CONFIG_RGBA_8888;
case RGBA_F16: return BITMAP_CONFIG_RGBA_F16;
case HARDWARE: return BITMAP_CONFIG_HARDWARE;
}
return BITMAP_CONFIG_RGBA_8888;
}
private static native void nSetBitmap(long nativeTexture, long nativeEngine,
int level, int xoffset, int yoffset, int width, int height, Bitmap bitmap, int format);
}

View File

@@ -126,11 +126,6 @@ public class UiHelper {
// TODO: do something with policy
}
public UiHelper(int sampleCount, RendererCallback renderCallback) {
this();
setRenderCallback(renderCallback);
}
public void setRenderCallback(RendererCallback renderCallback) {
mRenderCallback = renderCallback;
}

View File

@@ -5,7 +5,8 @@ Filament APIs:
- `hello-triangle`: Minimal example showing how to setup a rendering surface for Filament
- `lit-cube`: Shows how to create a light and a mesh with the attributes required for lighting
- `image-based-lighting`: Demonstrates how to create image-based lights and load complex meshes
- `image-based-lighting`: Demonstrates how to create image-based lights and load complex meshes
- `textured-object`: Demonstrates how to load and use textures for complex materials
## Prerequisites

View File

@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.70'
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -49,11 +49,11 @@ fun destroyIbl(engine: Engine, ibl: Ibl) {
}
private fun peekSize(assets: AssetManager, name: String): Pair<Int, Int> {
val input = assets.open(name)
val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeStream(input, null, opts)
input.close()
return opts.outWidth to opts.outHeight
assets.open(name).use { input ->
val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeStream(input, null, opts)
return opts.outWidth to opts.outHeight
}
}
private fun loadIndirectLight(
@@ -70,7 +70,7 @@ private fun loadIndirectLight(
.sampler(Texture.Sampler.SAMPLER_CUBEMAP)
.build(engine)
(0 until texture.levels).forEach {
repeat(texture.levels) {
loadCubemap(texture, assets, name, engine, "m${it}_", it)
}
@@ -88,7 +88,7 @@ private fun loadSphericalHarmonics(assets: AssetManager, name: String): FloatArr
// 3 bands = 9 RGB coefficients, so 9 * 3 floats
val sphericalHarmonics = FloatArray(9 * 3)
BufferedReader(InputStreamReader(assets.open("$name/sh.txt"))).use { input ->
(0 until 9).forEach { i ->
repeat(9) { i ->
val line = input.readLine()
re.find(line)?.let {
sphericalHarmonics[i * 3] = it.groups[1]?.value?.toFloat() ?: 0.0f
@@ -133,7 +133,7 @@ private fun loadCubemap(texture: Texture,
// Allocate enough memory for all the cubemap faces
val storage = ByteBuffer.allocateDirect(faceSize * 6)
arrayOf("px", "nx", "py", "ny", "pz", "nz").forEachIndexed { _, suffix ->
arrayOf("px", "nx", "py", "ny", "pz", "nz").forEach { suffix ->
assets.open("$name/$prefix$suffix.rgbm").use {
val bitmap = BitmapFactory.decodeStream(it, null, opts)
bitmap?.copyPixelsToBuffer(storage)

View File

@@ -132,8 +132,8 @@ class MainActivity : Activity() {
setupMaterial()
loadImageBasedLight()
scene.setSkybox(ibl.skybox)
scene.setIndirectLight(ibl.indirectLight)
scene.skybox = ibl.skybox
scene.indirectLight = ibl.indirectLight
// This map can contain named materials that will map to the material names
// loaded from the filamesh file. The material called "DefaultMaterial" is

View File

@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.70'
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -291,7 +291,7 @@ class MainActivity : Activity() {
// Create the indices
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
.order(ByteOrder.nativeOrder())
(0..5).forEach {
repeat(5) {
val i = (it * 4).toShort()
indexData
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())

View File

@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.70'
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

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

View File

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

View File

@@ -0,0 +1,98 @@
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")
}
compileMesh {
group 'Filament'
description 'Compile mesh'
inputFile = file("../../../../third_party/shader_ball/shader_ball.obj")
outputDir = file("src/main/assets/models")
}
generateIbl {
group 'Filament'
description 'Generate IBL'
inputFile = file("../../../../third_party/environments/venetian_crossroads_2k.hdr")
outputDir = file("src/main/assets/envs")
}
preBuild.dependsOn compileMaterials
preBuild.dependsOn compileMesh
preBuild.dependsOn generateIbl
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.google.android.filament.textured"
minSdkVersion 26
targetSdkVersion 28
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'
}

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,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.textured">
<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="com.google.android.filament.textured.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,148 @@
/*
* Copyright (C) 2018 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.textured
import android.content.res.AssetManager
import android.graphics.BitmapFactory
import com.google.android.filament.Engine
import com.google.android.filament.IndirectLight
import com.google.android.filament.Skybox
import com.google.android.filament.Texture
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.ByteBuffer
import kotlin.math.log2
data class Ibl(val indirectLight: IndirectLight,
val indirectLightTexture: Texture,
val skybox: Skybox,
val skyboxTexture: Texture)
fun loadIbl(assets: AssetManager, name: String, engine: Engine): Ibl {
val (ibl, iblTexture) = loadIndirectLight(assets, name, engine)
val (skybox, skyboxTexture) = loadSkybox(assets, name, engine)
return Ibl(ibl, iblTexture, skybox, skyboxTexture)
}
fun destroyIbl(engine: Engine, ibl: Ibl) {
engine.destroySkybox(ibl.skybox)
engine.destroyTexture(ibl.skyboxTexture)
engine.destroyIndirectLight(ibl.indirectLight)
engine.destroyTexture(ibl.indirectLightTexture)
}
private fun peekSize(assets: AssetManager, name: String): Pair<Int, Int> {
assets.open(name).use { input ->
val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeStream(input, null, opts)
return opts.outWidth to opts.outHeight
}
}
private fun loadIndirectLight(
assets: AssetManager,
name: String,
engine: Engine): Pair<IndirectLight, Texture> {
val (w, h) = peekSize(assets, "$name/nx.rgbm")
val texture = Texture.Builder()
.width(w)
.height(h)
.levels(log2(w.toFloat()).toInt() + 1)
.format(Texture.InternalFormat.RGBA8)
.rgbm(true)
.sampler(Texture.Sampler.SAMPLER_CUBEMAP)
.build(engine)
repeat(texture.levels) {
loadCubemap(texture, assets, name, engine, "m${it}_", it)
}
val sphericalHarmonics = loadSphericalHarmonics(assets, name)
return IndirectLight.Builder()
.reflections(texture)
.irradiance(3, sphericalHarmonics)
.intensity(30_000.0f)
.build(engine) to texture
}
private fun loadSphericalHarmonics(assets: AssetManager, name: String): FloatArray {
val re = Regex("""\(\s*([+-]?\d+\.\d+),\s*([+-]?\d+\.\d+),\s*([+-]?\d+\.\d+)\);""")
// 3 bands = 9 RGB coefficients, so 9 * 3 floats
val sphericalHarmonics = FloatArray(9 * 3)
BufferedReader(InputStreamReader(assets.open("$name/sh.txt"))).use { input ->
repeat(9) { i ->
val line = input.readLine()
re.find(line)?.let {
sphericalHarmonics[i * 3] = it.groups[1]?.value?.toFloat() ?: 0.0f
sphericalHarmonics[i * 3 + 1] = it.groups[2]?.value?.toFloat() ?: 0.0f
sphericalHarmonics[i * 3 + 2] = it.groups[3]?.value?.toFloat() ?: 0.0f
}
}
}
return sphericalHarmonics
}
private fun loadSkybox(assets: AssetManager, name: String, engine: Engine): Pair<Skybox, Texture> {
val (w, h) = peekSize(assets, "$name/nx.rgbm")
val texture = Texture.Builder()
.width(w)
.height(h)
.levels(1)
.format(Texture.InternalFormat.RGBA8)
.rgbm(true)
.sampler(Texture.Sampler.SAMPLER_CUBEMAP)
.build(engine)
loadCubemap(texture, assets, name, engine)
return Skybox.Builder().environment(texture).build(engine) to texture
}
private fun loadCubemap(texture: Texture,
assets: AssetManager,
name: String,
engine: Engine,
prefix: String = "",
level: Int = 0) {
// This is important, in the RGBM format the alpha channel does not encode
// opacity but a scale factor (to represent HDR data). We must tell Android
// to not premultiply the RGB channels by the alpha channel
val opts = BitmapFactory.Options().apply { inPremultiplied = false }
// RGBM is always 4 bytes per pixel
val faceSize = texture.getWidth(level) * texture.getHeight(level) * 4
val offsets = IntArray(6) { it * faceSize }
// Allocate enough memory for all the cubemap faces
val storage = ByteBuffer.allocateDirect(faceSize * 6)
arrayOf("px", "nx", "py", "ny", "pz", "nz").forEach { suffix ->
assets.open("$name/$prefix$suffix.rgbm").use {
val bitmap = BitmapFactory.decodeStream(it, null, opts)
bitmap?.copyPixelsToBuffer(storage)
}
}
// Rewind the texture buffer
storage.flip()
val buffer = Texture.PixelBufferDescriptor(storage, Texture.Format.RGBM, Texture.Type.UBYTE)
texture.setImage(engine, level, buffer, offsets)
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2018 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.textured
import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
fun readIntLE(input: InputStream): Int {
return input.read() and 0xff or (
input.read() and 0xff shl 8) or (
input.read() and 0xff shl 16) or (
input.read() and 0xff shl 24)
}
fun readFloat32LE(input: InputStream): Float {
val bytes = ByteArray(4)
input.read(bytes, 0, 4)
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).float
}
fun readUIntLE(input: InputStream): Long {
return readIntLE(input).toLong() and 0xFFFFFFFFL
}

View File

@@ -0,0 +1,337 @@
/*
* Copyright (C) 2018 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.textured
import android.animation.ValueAnimator
import android.app.Activity
import android.os.Bundle
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceView
import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.android.UiHelper
import java.nio.ByteBuffer
import java.nio.channels.Channels
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class MainActivity : Activity() {
// Make sure to initialize Filament first
// This loads the JNI library needed by most API calls
companion object {
init {
Filament.init()
}
}
// The View we want to render into
private lateinit var surfaceView: SurfaceView
// UiHelper is provided by Filament to manage SurfaceView and SurfaceTexture
private lateinit var uiHelper: UiHelper
// Choreographer is used to schedule new frames
private lateinit var choreographer: Choreographer
// Engine creates and destroys Filament resources
// Each engine must be accessed from a single thread of your choosing
// Resources cannot be shared across engines
private lateinit var engine: Engine
// A renderer instance is tied to a single surface (SurfaceView, TextureView, etc.)
private lateinit var renderer: Renderer
// A scene holds all the renderable, lights, etc. to be drawn
private lateinit var scene: Scene
// A view defines a viewport, a scene and a camera for rendering
private lateinit var view: View
// Should be pretty obvious :)
private lateinit var camera: Camera
private lateinit var material: Material
private lateinit var materialInstance: MaterialInstance
private lateinit var baseColor: Texture
private lateinit var normal: Texture
private lateinit var aoRoughnessMetallic: Texture
private lateinit var mesh: Mesh
private lateinit var ibl: Ibl
// Filament entity representing a renderable object
@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, (2.0 * PI).toFloat())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
surfaceView = SurfaceView(this)
setContentView(surfaceView)
choreographer = Choreographer.getInstance()
setupSurfaceView()
setupFilament()
setupView()
setupScene()
}
private fun setupSurfaceView() {
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
uiHelper.renderCallback = SurfaceCallback()
// NOTE: To choose a specific rendering resolution, add the following line:
// uiHelper.setDesiredSize(1280, 720)
uiHelper.attachTo(surfaceView)
}
private fun setupFilament() {
engine = Engine.create()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
camera = engine.createCamera()
}
private fun setupView() {
// Clear the background to middle-grey
// Setting up a clear color is useful for debugging but usually
// unnecessary when using a skybox
view.setClearColor(0.035f, 0.035f, 0.035f, 1.0f)
// NOTE: Try to disable post-processing (tone-mapping, etc.) to see the difference
// view.isPostProcessingEnabled = false
// Tell the view which camera we want to use
view.camera = camera
// Tell the view which scene we want to render
view.scene = scene
// Enable dynamic resolution with a default target frame rate of 60fps
val options = View.DynamicResolutionOptions()
options.enabled = true
view.dynamicResolutionOptions = options
}
private fun setupScene() {
loadMaterial()
setupMaterial()
loadImageBasedLight()
scene.skybox = ibl.skybox
scene.indirectLight = ibl.indirectLight
// This map can contain named materials that will map to the material names
// loaded from the filamesh file. The material called "DefaultMaterial" is
// applied when no named material can be found
val materials = mapOf("DefaultMaterial" to materialInstance)
// Load the mesh in the filamesh format (see filamesh tool)
mesh = loadMesh(assets, "models/shader_ball.filamesh", materials, engine)
// Move the mesh down
// Filament uses column-major matrices
engine.transformManager.setTransform(engine.transformManager.getInstance(mesh.renderable),
floatArrayOf(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, -1.2f, 0.0f, 1.0f
))
// Add the entity to the scene to render it
scene.addEntity(mesh.renderable)
// We now need a light, let's create a directional light
light = EntityManager.get().create()
// Create a color from a temperature (D65)
val (r, g, b) = Colors.cct(6_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.753f, -1.0f, 0.890f)
.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)
startAnimation()
}
private fun loadMaterial() {
readUncompressedAsset("materials/textured_pbr.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
}
}
private fun setupMaterial() {
// Create an instance of the material to set different parameters on it
materialInstance = material.createInstance()
// Note that the textures are stored in drawable-nodpi to prevent the system
// from automatically resizing them based on the display's density
baseColor = loadTexture(engine, resources, R.drawable.floor_basecolor, TextureType.COLOR)
normal = loadTexture(engine, resources, R.drawable.floor_normal, TextureType.NORMAL)
aoRoughnessMetallic = loadTexture(engine, resources,
R.drawable.floor_ao_roughness_metallic, TextureType.DATA)
// A texture sampler does not need to be kept around or destroyed
val sampler = TextureSampler()
sampler.anisotropy = 8.0f
materialInstance.setParameter("baseColor", baseColor, sampler)
materialInstance.setParameter("normal", normal, sampler)
materialInstance.setParameter("aoRoughnessMetallic", aoRoughnessMetallic, sampler)
}
private fun loadImageBasedLight() {
ibl = loadIbl(assets, "envs/venetian_crossroads_2k", engine)
ibl.indirectLight.intensity = 40_000.0f
}
private fun startAnimation() {
// Animate the triangle
animator.interpolator = LinearInterpolator()
animator.duration = 18_000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener { a ->
val v = (a.animatedValue as Float)
camera.lookAt(cos(v) * 4.5, 1.5, sin(v) * 4.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
}
animator.start()
}
override fun onResume() {
super.onResume()
choreographer.postFrameCallback(frameScheduler)
animator.start()
}
override fun onPause() {
super.onPause()
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
}
override fun onDestroy() {
super.onDestroy()
// 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
Fence.waitAndDestroy(engine.createFence(Fence.Type.SOFT), Fence.Mode.FLUSH)
// Cleanup all resources
destroyMesh(engine, mesh)
destroyIbl(engine, ibl)
engine.destroyTexture(baseColor)
engine.destroyTexture(normal)
engine.destroyTexture(aoRoughnessMetallic)
engine.destroyEntity(light)
engine.destroyRenderer(renderer)
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)
// 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(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() }
}
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright (C) 2018 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.textured
import android.content.res.AssetManager
import android.util.Log
import com.google.android.filament.*
import com.google.android.filament.VertexBuffer.AttributeType.*
import com.google.android.filament.VertexBuffer.VertexAttribute.*
import java.io.InputStream
import java.nio.charset.Charset
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.Channels
import java.nio.channels.ReadableByteChannel
data class Mesh(
@Entity val renderable: Int,
val indexBuffer: IndexBuffer,
val vertexBuffer: VertexBuffer,
val aabb: Box)
fun destroyMesh(engine: Engine, mesh: Mesh) {
engine.destroyEntity(mesh.renderable)
engine.destroyIndexBuffer(mesh.indexBuffer)
engine.destroyVertexBuffer(mesh.vertexBuffer)
EntityManager.get().destroy(mesh.renderable)
}
fun loadMesh(assets: AssetManager, name: String,
materials: Map<String, MaterialInstance>, engine: Engine): Mesh {
// See tools/filamesh/README.md for a description of the filamesh file format
assets.open(name).use { input ->
val header = readHeader(input)
val channel = Channels.newChannel(input)
val vertexBufferData = readSizedData(channel, header.verticesSizeInBytes)
val indexBufferData = readSizedData(channel, header.indicesSizeInBytes)
val parts = readParts(header, input)
val definedMaterials = readMaterials(input)
val indexBuffer = createIndexBuffer(engine, header, indexBufferData)
val vertexBuffer = createVertexBuffer(engine, header, vertexBufferData)
val renderable = createRenderable(
engine, header, indexBuffer, vertexBuffer, parts, definedMaterials, materials)
return Mesh(renderable, indexBuffer, vertexBuffer, header.aabb)
}
}
private const val FILAMESH_FILE_IDENTIFIER = "FILAMESH"
private const val MAX_UINT32 = 4294967295
private class Header {
var valid = false
var versionNumber = 0L
var parts = 0L
var aabb = Box()
var interleaved = 0L
var posOffset = 0L
var positionStride = 0L
var tangentOffset = 0L
var tangentStride = 0L
var colorOffset = 0L
var colorStride = 0L
var uv0Offset = 0L
var uv0Stride = 0L
var uv1Offset = 0L
var uv1Stride = 0L
var totalVertices = 0L
var verticesSizeInBytes = 0L
var indices16Bit = 0L
var totalIndices = 0L
var indicesSizeInBytes = 0L
}
private class Part {
var offset = 0L
var indexCount = 0L
var minIndex = 0L
var maxIndex = 0L
var materialID = 0L
var aabb = Box()
}
private fun readMagicNumber(input: InputStream): Boolean {
val temp = ByteArray(FILAMESH_FILE_IDENTIFIER.length)
input.read(temp)
val tempS = String(temp, Charset.forName("UTF-8"))
return tempS == FILAMESH_FILE_IDENTIFIER
}
private fun readHeader(input: InputStream): Header {
val header = Header()
if (!readMagicNumber(input)) {
Log.e("Filament", "Invalid filamesh file.")
return header
}
header.versionNumber = readUIntLE(input)
header.parts = readUIntLE(input)
header.aabb = Box(
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input),
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input))
header.interleaved = readUIntLE(input)
header.posOffset = readUIntLE(input)
header.positionStride = readUIntLE(input)
header.tangentOffset = readUIntLE(input)
header.tangentStride = readUIntLE(input)
header.colorOffset = readUIntLE(input)
header.colorStride = readUIntLE(input)
header.uv0Offset = readUIntLE(input)
header.uv0Stride = readUIntLE(input)
header.uv1Offset = readUIntLE(input)
header.uv1Stride = readUIntLE(input)
header.totalVertices = readUIntLE(input)
header.verticesSizeInBytes = readUIntLE(input)
header.indices16Bit = readUIntLE(input)
header.totalIndices = readUIntLE(input)
header.indicesSizeInBytes = readUIntLE(input)
header.valid = true
return header
}
private fun readSizedData(channel: ReadableByteChannel, sizeInBytes: Long): ByteBuffer {
val buffer = ByteBuffer.allocateDirect(sizeInBytes.toInt())
buffer.order(ByteOrder.LITTLE_ENDIAN)
channel.read(buffer)
buffer.flip()
return buffer
}
private fun readParts(header: Header, input: InputStream): List<Part> {
return List(header.parts.toInt()) {
val p = Part()
p.offset = readUIntLE(input)
p.indexCount = readUIntLE(input)
p.minIndex = readUIntLE(input)
p.maxIndex = readUIntLE(input)
p.materialID = readUIntLE(input)
p.aabb = Box(
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input),
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input))
p
}
}
private fun readMaterials(input: InputStream): List<String> {
return List(readUIntLE(input).toInt()) {
val data = ByteArray(readUIntLE(input).toInt())
input.read(data)
// Skip null terminator
input.skip(1)
data.toString(Charset.forName("UTF-8"))
}
}
private fun createIndexBuffer(engine: Engine, header: Header, data: ByteBuffer): IndexBuffer {
val indexType = if (header.indices16Bit != 0L) {
IndexBuffer.Builder.IndexType.USHORT
} else {
IndexBuffer.Builder.IndexType.UINT
}
return IndexBuffer.Builder()
.bufferType(indexType)
.indexCount(header.totalIndices.toInt())
.build(engine)
.apply { setBuffer(engine, data) }
}
private fun createVertexBuffer(engine: Engine, header: Header, data: ByteBuffer): VertexBuffer {
val vertexBufferBuilder = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(header.totalVertices.toInt())
// We store colors as unsigned bytes (0..255) but the shader wants values in the 0..1
// range so we must mark this attribute normalized
.normalized(COLOR)
// The same goes for the tangent frame: we store it as a signed short, but we want
// values in 0..1 in the shader
.normalized(TANGENTS)
.attribute(POSITION, 0, HALF4, header.posOffset.toInt(), header.positionStride.toInt())
.attribute(TANGENTS, 0, SHORT4, header.tangentOffset.toInt(), header.tangentStride.toInt())
.attribute(COLOR, 0, UBYTE4, header.colorOffset.toInt(), header.colorStride.toInt())
// UV coordinates are stored in fp16, which gives sub-pixel precision only
// for textures up to 1024x1024
.attribute(UV0, 0, HALF2, header.uv0Offset.toInt(), header.uv0Stride.toInt())
if (header.uv1Offset != MAX_UINT32 && header.uv1Stride != MAX_UINT32) {
vertexBufferBuilder
.attribute(UV1, 0, HALF2, header.uv1Offset.toInt(), header.uv1Stride.toInt())
}
return vertexBufferBuilder.build(engine).apply { setBufferAt(engine, 0, data) }
}
private fun createRenderable(
engine: Engine,
header: Header,
indexBuffer: IndexBuffer,
vertexBuffer: VertexBuffer,
parts: List<Part>,
definedMaterials: List<String>,
materials: Map<String, MaterialInstance>): Int {
val builder = RenderableManager.Builder(header.parts.toInt()).boundingBox(header.aabb)
repeat(header.parts.toInt()) { i ->
builder.geometry(i,
RenderableManager.PrimitiveType.TRIANGLES,
vertexBuffer,
indexBuffer,
parts[i].offset.toInt(),
parts[i].minIndex.toInt(),
parts[i].maxIndex.toInt(),
parts[i].indexCount.toInt())
// Find a material in the supplied material map, otherwise we fall back to
// the default material named "DefaultMaterial"
val material = materials[definedMaterials[parts[i].materialID.toInt()]]
material?.let {
builder.material(i, material)
} ?: builder.material(i, materials["DefaultMaterial"]!!)
}
return EntityManager.get().create().apply { builder.build(engine, this) }
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2018 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.textured
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import com.google.android.filament.Engine
import com.google.android.filament.Texture
import com.google.android.filament.android.TextureHelper
import java.lang.IllegalArgumentException
import java.nio.ByteBuffer
const val SKIP_BITMAP_COPY = true
enum class TextureType {
COLOR,
NORMAL,
DATA
}
fun loadTexture(engine: Engine, resources: Resources, resourceId: Int, type: TextureType): Texture {
val options = BitmapFactory.Options()
// Color is the only type of texture we want to pre-multiply with the alpha channel
// Pre-multiplication is the default behavior, so we need to turn it off here
options.inPremultiplied = type == TextureType.COLOR
val bitmap = BitmapFactory.decodeResource(resources, resourceId, options)
val texture = Texture.Builder()
.width(bitmap.width)
.height(bitmap.height)
.sampler(Texture.Sampler.SAMPLER_2D)
.format(internalFormat(type))
// This tells Filament to figure out the number of mip levels
.levels(0xff)
.build(engine)
// TextureHelper offers a method that skips the copy of the bitmap into a ByteBuffer
if (SKIP_BITMAP_COPY) {
TextureHelper.setBitmap(engine, texture, 0, bitmap)
} else {
val buffer = ByteBuffer.allocateDirect(bitmap.byteCount)
bitmap.copyPixelsToBuffer(buffer)
// Do not forget to rewind the buffer!
buffer.flip()
val descriptor = Texture.PixelBufferDescriptor(
buffer,
format(bitmap),
type(bitmap))
texture.setImage(engine, 0, descriptor)
}
texture.generateMipmaps(engine)
return texture
}
private fun internalFormat(type: TextureType) = when (type) {
TextureType.COLOR -> Texture.InternalFormat.SRGB8_A8
TextureType.NORMAL -> Texture.InternalFormat.RGBA8
TextureType.DATA -> Texture.InternalFormat.RGBA8
}
// Not required when SKIP_BITMAP_COPY is true
private fun format(bitmap: Bitmap) = when (bitmap.config) {
Bitmap.Config.ALPHA_8 -> Texture.Format.ALPHA
Bitmap.Config.RGB_565 -> Texture.Format.RGB
Bitmap.Config.ARGB_8888 -> Texture.Format.RGBA
Bitmap.Config.RGBA_F16 -> Texture.Format.RGBA
else -> throw IllegalArgumentException("Unknown bitmap configuration")
}
// Not required when SKIP_BITMAP_COPY is true
private fun type(bitmap: Bitmap) = when (bitmap.config) {
Bitmap.Config.ALPHA_8 -> Texture.Type.UBYTE
Bitmap.Config.RGB_565 -> Texture.Type.UBYTE
Bitmap.Config.ARGB_8888 -> Texture.Type.UBYTE
Bitmap.Config.RGBA_F16 -> Texture.Type.HALF
else -> throw IllegalArgumentException("Unsupported bitmap configuration")
}

View File

@@ -0,0 +1,44 @@
// Textured material
material {
name : textured_pbr,
shadingModel : lit,
parameters : [
// Base color sRGB texture
{
type : sampler2d,
name : baseColor
},
// Packed ambient occlusion/roughness/metallic RGB texture
{
type : sampler2d,
name : aoRoughnessMetallic
},
// Normal map RGB texture
{
type : sampler2d,
name : normal
}
],
// To sample textures our material must declare that it requires
// a set of UV coordinates from the rendered mesh
requires: [
uv0
]
}
fragment {
void material(inout MaterialInputs material) {
// The normal map must be set *before* calling prepareMaterial()
material.normal = texture(materialParams_normal, getUV0()).xyz * 2.0 - 1.0;
prepareMaterial(material);
material.baseColor = texture(materialParams_baseColor, getUV0());
vec3 aoRoughnessMetallic = texture(materialParams_aoRoughnessMetallic, getUV0()).rgb;
material.ambientOcclusion = aoRoughnessMetallic.r;
material.roughness = aoRoughnessMetallic.g;
material.metallic = aoRoughnessMetallic.b;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

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">Textured Object</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,27 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
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 @@
#Tue Aug 28 15:45:13 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

172
android/samples/textured-object/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'

156
build.sh
View File

@@ -3,8 +3,9 @@ set -e
# NDK API level
API_LEVEL=24
# Host tools required by Android and WebGL builds
# Host tools required by Android, WebGL, and iOS builds
HOST_TOOLS="matc cmgen filamesh mipgen"
IOS_TOOLCHAIN_URL="https://opensource.apple.com/source/clang/clang-800.0.38/src/cmake/platforms/iOS.cmake"
function print_help {
local SELF_NAME=`basename $0`
@@ -26,10 +27,10 @@ function print_help {
echo " Do not compile desktop Java projects"
echo " -m"
echo " Compile with make instead of ninja."
echo " -p [desktop|android|webgl|all]"
echo " -p [desktop|android|ios|webgl|all]"
echo " Platform(s) to build, defaults to desktop."
echo " Building android will automatically generate the toolchains if needed and"
echo " perform a partial desktop build."
echo " Building for Android or iOS will automatically generate / download"
echo " the toolchains if needed and perform a partial desktop build."
echo " -r"
echo " Restrict the number of make/ninja jobs."
echo " -t"
@@ -38,6 +39,8 @@ function print_help {
echo " Run all unit tests, will trigger a debug build if needed."
echo " -v"
echo " Add Vulkan support to the Android build."
echo " -s"
echo " Add iOS simulator support to the iOS build."
echo ""
echo "Build types:"
echo " release"
@@ -71,6 +74,7 @@ ISSUE_DEBUG_BUILD=false
ISSUE_RELEASE_BUILD=false
ISSUE_ANDROID_BUILD=false
ISSUE_IOS_BUILD=false
ISSUE_DESKTOP_BUILD=true
ISSUE_WEBGL_BUILD=false
@@ -88,6 +92,8 @@ GENERATE_TOOLCHAINS=false
VULKAN_ANDROID_OPTION="-DFILAMENT_SUPPORTS_VULKAN=OFF"
IOS_BUILD_SIMULATOR=false
BUILD_GENERATOR=Ninja
BUILD_COMMAND=ninja
BUILD_CUSTOM_TARGETS=
@@ -213,12 +219,32 @@ function build_webgl_with_target {
${BUILD_COMMAND} ${BUILD_TARGETS}
fi
if [ -d "samples/web/public" ]; then
if [ -d "web/filament-js" ]; then
if [ "$ISSUE_ARCHIVES" == "true" ]; then
which -s python3
if [ $? == 0 ]; then
echo "Generating JavaScript documentation..."
local DOCS_FOLDER="web/docs"
local DOCS_SCRIPT="../../web/docs/build.py"
python3 ${DOCS_SCRIPT} --disable-demo \
--output-folder ${DOCS_FOLDER} \
--build-folder ${PWD}
fi
echo "Generating out/filament-${LC_TARGET}-web.tgz..."
cd samples/web/public
tar -czvf ../../../../filament-${LC_TARGET}-web.tgz .
# The web archive has the following subfolders:
# dist...core WASM module and accompanying JS file.
# docs...HTML tutorials for the JS API, accompanying demos, and a reference page.
cd web
tar -cvf ../../filament-${LC_TARGET}-web.tar -s /^filament-js/dist/ \
filament-js/filament.js
tar -rvf ../../filament-${LC_TARGET}-web.tar -s /^filament-js/dist/ \
filament-js/filament.wasm
tar -rvf ../../filament-${LC_TARGET}-web.tar docs
cd -
gzip -c ../filament-${LC_TARGET}-web.tar > ../filament-${LC_TARGET}-web.tgz
rm ../filament-${LC_TARGET}-web.tar
fi
fi
@@ -346,6 +372,100 @@ function build_android {
cd ../..
}
function ensure_ios_toolchain {
local TOOLCHAIN_PATH="build/toolchain-mac-ios.cmake"
if [ -e ${TOOLCHAIN_PATH} ]; then
echo "iOS toolchain file exists."
return 0
fi
echo
echo "iOS toolchain file does not exist."
echo "It will automatically be downloaded from http://opensource.apple.com."
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
echo "Toolchain file must be downloaded to continue."
exit 1
fi
curl -o "${TOOLCHAIN_PATH}" "${IOS_TOOLCHAIN_URL}" || {
echo "Error downloading iOS toolchain file."
exit 1
}
# Apple's toolchain hard-codes the PLATFORM_NAME into the toolchain file. Instead, make this a
# CACHE variable that can be overriden on the command line.
local FIND='SET(PLATFORM_NAME iphoneos)'
local REPLACE='SET(PLATFORM_NAME "iphoneos" CACHE STRING "iOS platform to build for")'
sed -i '' "s/${FIND}/${REPLACE}/g" ./${TOOLCHAIN_PATH}
# Append Filament-specific settings.
cat build/toolchain-mac-ios.filament.cmake >> ${TOOLCHAIN_PATH}
echo "Successfully downloaded iOS toolchain file and appended Filament-specific settings."
}
function build_ios_target {
local LC_TARGET=`echo $1 | tr '[:upper:]' '[:lower:]'`
local ARCH=$2
local PLATFORM=$3
echo "Building iOS $LC_TARGET ($ARCH) for $PLATFORM..."
mkdir -p out/cmake-ios-${LC_TARGET}-${ARCH}
cd out/cmake-ios-${LC_TARGET}-${ARCH}
if [ ! -d "CMakeFiles" ] || [ "$ISSUE_CMAKE_ALWAYS" == "true" ]; then
cmake \
-G "$BUILD_GENERATOR" \
-DCMAKE_BUILD_TYPE=$1 \
-DCMAKE_INSTALL_PREFIX=../ios-${LC_TARGET}/filament \
-DIOS_ARCH=${ARCH} \
-DPLATFORM_NAME=${PLATFORM} \
-DIOS=1 \
-DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-mac-ios.cmake \
../..
fi
${BUILD_COMMAND} install
if [ -d "../ios-${LC_TARGET}/filament" ]; then
if [ "$ISSUE_ARCHIVES" == "true" ]; then
echo "Generating out/filament-${LC_TARGET}-ios.tgz..."
cd ../ios-${LC_TARGET}
tar -czvf ../filament-${LC_TARGET}-ios.tgz filament
fi
fi
cd ../..
}
function build_ios {
# Supress intermediate desktop tools install
OLD_INSTALL_COMMAND=${INSTALL_COMMAND}
INSTALL_COMMAND=
build_desktop "${HOST_TOOLS}"
INSTALL_COMMAND=${OLD_INSTALL_COMMAND}
ensure_ios_toolchain
# In theory, we could support iPhone architectures older than arm64, but
# only arm64 devices support OpenGL 3.0 / Metal
if [ "$ISSUE_DEBUG_BUILD" == "true" ]; then
build_ios_target "Debug" "arm64" "iphoneos"
if [ "$IOS_BUILD_SIMULATOR" == "true" ]; then
build_ios_target "Debug" "x86_64" "iphonesimulator"
fi
fi
if [ "$ISSUE_RELEASE_BUILD" == "true" ]; then
build_ios_target "Release" "arm64" "iphoneos"
if [ "$IOS_BUILD_SIMULATOR" == "true" ]; then
build_ios_target "Release" "x86_64" "iphonesimulator"
fi
fi
}
function validate_build_command {
set +e
# Make sure CMake is installed
@@ -354,7 +474,7 @@ function validate_build_command {
echo "Error: could not find cmake, exiting"
exit 1
fi
# Make sure Ninja is installed
if [ "$BUILD_COMMAND" == "ninja" ]; then
ninja_binary=`which ninja`
@@ -405,7 +525,7 @@ function run_tests {
pushd `dirname $0` > /dev/null
while getopts ":hacfijmp:tuv" opt; do
while getopts ":hacfijmp:tuvs" opt; do
case ${opt} in
h)
print_help
@@ -435,21 +555,31 @@ while getopts ":hacfijmp:tuv" opt; do
case $OPTARG in
desktop)
ISSUE_ANDROID_BUILD=false
ISSUE_IOS_BUILD=false
ISSUE_DESKTOP_BUILD=true
ISSUE_WEBGL_BUILD=false
;;
android)
ISSUE_ANDROID_BUILD=true
ISSUE_IOS_BUILD=false
ISSUE_DESKTOP_BUILD=false
ISSUE_WEBGL_BUILD=false
;;
ios)
ISSUE_ANDROID_BUILD=false
ISSUE_IOS_BUILD=true
ISSUE_DESKTOP_BUILD=false
ISSUE_WEBGL_BUILD=false
;;
webgl)
ISSUE_ANDROID_BUILD=false
ISSUE_IOS_BUILD=false
ISSUE_DESKTOP_BUILD=false
ISSUE_WEBGL_BUILD=true
;;
all)
ISSUE_ANDROID_BUILD=true
ISSUE_IOS_BUILD=true
ISSUE_DESKTOP_BUILD=true
ISSUE_WEBGL_BUILD=false
;;
@@ -472,6 +602,10 @@ while getopts ":hacfijmp:tuv" opt; do
echo "Also be sure to pass Backend::VULKAN to Engine::create."
echo ""
;;
s)
IOS_BUILD_SIMULATOR=true
echo "iOS simulator support enabled."
;;
\?)
echo "Invalid option: -$OPTARG" >&2
echo ""
@@ -522,6 +656,10 @@ if [ "$ISSUE_ANDROID_BUILD" == "true" ]; then
build_android
fi
if [ "$ISSUE_IOS_BUILD" == "true" ]; then
build_ios
fi
if [ "$ISSUE_WEBGL_BUILD" == "true" ]; then
build_webgl
fi

2
build/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# File is generated by ../build.sh and should not be tracked
toolchain-mac-ios.cmake

22
build/ios/build.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
# build.sh prompts the user to download Apple's iOS toolchain
yes | ./build.sh -p ios -c $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE

6
build/ios/ci-common.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip
unzip -q ninja-mac.zip
chmod +x ninja
export PATH="$PWD:$PATH"

10
build/ios/common.cfg Normal file
View File

@@ -0,0 +1,10 @@
# Format: //devtools/kokoro/config/proto/build.proto
action {
define_artifacts {
regex: "github/filament/out/*.tgz"
regex: "github/filament/out/*.aar"
strip_prefix: "github/filament/out"
regex: "**/*sponge_log.xml"
}
}

3
build/ios/continuous.cfg Normal file
View File

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

3
build/ios/presubmit.cfg Normal file
View File

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

3
build/ios/release.cfg Normal file
View File

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

View File

@@ -0,0 +1,15 @@
# ==================================================================================================
# Filament-specific settings
# The rest of the toolchain is downloaded from Apple Open Source and appended.
# ==================================================================================================
set(CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
# Necessary for correct install location
set(DIST_ARCH ${IOS_ARCH})
add_definitions(-DIOS)
# ==================================================================================================
# End Filament-specific settings
# ==================================================================================================

View File

@@ -5,6 +5,11 @@ unzip -q ninja-mac.zip
chmod +x ninja
export PATH="$PWD:$PATH"
# The Kokoro machines have Python 3.6.3 installed. Let's verify that here, and install the
# distributions required for web/docs.
python3 --version
pip3 install mistletoe pygments jsbeautifier
curl -L https://github.com/juj/emsdk/archive/0d8576c.zip > emsdk.zip
unzip emsdk.zip
mv emsdk-* emsdk

View File

@@ -23,6 +23,9 @@ mkdir out\cmake-release
cd out\cmake-release
if errorlevel 1 exit /b %errorlevel%
:: Force Java JDK 8. Kokoro machines default to 9
SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0_152
cmake ..\.. -G Ninja ^
-DCMAKE_CXX_COMPILER:PATH="clang-cl.exe" ^
-DCMAKE_C_COMPILER:PATH="clang-cl.exe" ^

View File

@@ -1,6 +1,11 @@
if defined KOKORO_BUILD_ID (
choco install llvm --version 6.0.1 -y
if errorlevel 1 exit /b %errorlevel%
:: retry if choco install failed
if errorlevel 1 (
choco install llvm --version 6.0.1 -y
if errorlevel 1 exit /b %errorlevel%
)
:: refreshenv is necessary to put LLVM on path
refreshenv

View File

@@ -3402,7 +3402,7 @@ To compute the specular color of a material we need to evaluate the complex Fres
We then sum (integrate) and normalize all the samples to obtain $\fNormal$ in the XYZ color space. From there, a simple color space conversion yields a linear sRGB color or a non-linear sRGB color after applying the opto-electronic transfer function (OETF, commonly known as "gamma" curve). Note that for some materials such as gold the final sRGB color might fall out of gamut. We use a simple normalization step as a cheap form of gamut remapping but it would be interesting to consider computing values in a color space with a wider gamut (for instance BT.2020).
To achieve the desired result we used the ICE 1931 2 degrees CMFs, from 360nm to 830nm at 1nm intervals ([source](http://cvrl.ioo.ucl.ac.uk/cmfs.htm)), and the CIE Standard Illuminant D65 relative spectral power distribution, from 300nm to 830nm, at 5nm intervals ([source](https://cielab.xyz/pdf/CIE_sel_colorimetric_tables.xls)).
To achieve the desired result we used the ICE 1931 2 degrees CMFs, from 360nm to 830nm at 1nm intervals ([source](http://cvrl.ioo.ucl.ac.uk/cmfs.htm)), and the CIE Standard Illuminant D65 relative spectral power distribution, from 300nm to 830nm, at 5nm intervals ([source](http://files.cie.co.at/204.xls)).
Our implementation is presented in listing [specularColorImpl], with the actual data omitted for brevity.

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -74,7 +74,6 @@ set(SRCS
src/Frustum.cpp
src/IndexBuffer.cpp
src/IndirectLight.cpp
src/GpuLightBuffer.cpp
src/Material.cpp
src/MaterialInstance.cpp
src/PostProcessManager.cpp
@@ -110,7 +109,6 @@ set(PRIVATE_HDRS
src/details/Froxelizer.h
src/details/IndexBuffer.h
src/details/IndirectLight.h
src/details/GpuLightBuffer.h
src/details/Material.h
src/details/MaterialInstance.h
src/details/RenderPrimitive.h
@@ -158,9 +156,14 @@ if (CMAKE_BUILD_TYPE MATCHES Debug)
list(APPEND SRCS src/driver/noop/NoopDriver.cpp)
endif()
if(IOS AND NOT FILAMENT_SUPPORTS_VULKAN)
message(FATAL_ERROR "Filament for iOS must be built with Vulkan support.")
endif()
# ==================================================================================================
# OS specific
# ==================================================================================================
# Here set the architecture specific sources
if (USE_EXTERNAL_GLES3)
# Experimental feature; clients provide their own Context Manager.
@@ -169,6 +172,8 @@ elseif (ANDROID)
list(APPEND SRCS src/driver/android/ExternalTextureManagerAndroid.cpp)
list(APPEND SRCS src/driver/android/VirtualMachineEnv.cpp)
list(APPEND SRCS src/driver/opengl/PlatformEGL.cpp)
elseif (IOS)
list(APPEND SRCS src/driver/opengl/PlatformCocoaTouchGL.mm)
elseif (APPLE)
list(APPEND SRCS src/driver/opengl/PlatformCocoaGL.mm)
elseif (WEBGL)
@@ -187,8 +192,6 @@ endif()
# "2" corresponds to SYSTRACE_TAG_FILEMENT (See: utils/Systrace.h)
add_definitions(-DSYSTRACE_TAG=2 )
add_definitions(-DFILAMENT_DRIVER_SUPPORTS_OPENGL)
# ==================================================================================================
# Vulkan Sources
# ==================================================================================================
@@ -207,8 +210,10 @@ if (FILAMENT_SUPPORTS_VULKAN)
)
if (LINUX)
list(APPEND SRCS src/driver/vulkan/PlatformVkLinux.cpp)
elseif (APPLE)
elseif (APPLE AND NOT IOS)
list(APPEND SRCS src/driver/vulkan/PlatformVkCocoa.mm)
elseif (IOS)
list(APPEND SRCS src/driver/vulkan/PlatformVkCocoaTouch.mm)
elseif (ANDROID)
list(APPEND SRCS src/driver/vulkan/PlatformVkAndroid.cpp)
elseif (WIN32)
@@ -229,7 +234,7 @@ set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
file(MAKE_DIRECTORY "${GENERATION_ROOT}/generated/material/")
# Target system.
if (ANDROID OR WEBGL)
if (ANDROID OR WEBGL OR IOS)
set(MATC_TARGET mobile)
else()
set(MATC_TARGET desktop)
@@ -305,17 +310,11 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
# Dependencies
# ==================================================================================================
set(DARWIN_LINK_LIBRARIES bluegl bluevk)
set(WIN32_LINK_LIBRARIES bluegl)
if (ANDROID)
target_link_libraries(${TARGET} PUBLIC GLESv3 EGL android)
set(LINUX_LINK_LIBRARIES )
else()
set(LINUX_LINK_LIBRARIES bluegl)
endif()
if (APPLE)
if (APPLE AND NOT IOS)
target_link_libraries(${TARGET} PRIVATE "-framework Cocoa")
endif()
@@ -325,6 +324,11 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC image_headers)
# Android and iOS link directly against platform-specific OpenGL ES libraries
if(NOT IOS AND NOT ANDROID)
target_link_libraries(${TARGET} PRIVATE bluegl)
endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc)
endif()
@@ -333,12 +337,6 @@ if (LINUX)
target_link_libraries(${TARGET} PRIVATE dl)
endif()
target_link_libraries(${TARGET} PRIVATE
"$<$<PLATFORM_ID:Darwin>:${DARWIN_LINK_LIBRARIES}>"
"$<$<PLATFORM_ID:Linux>:${LINUX_LINK_LIBRARIES}>"
"$<$<PLATFORM_ID:Windows>:${WIN32_LINK_LIBRARIES}>"
)
# ==================================================================================================
# Compiler flags
# ==================================================================================================

View File

@@ -11,7 +11,7 @@ Filament. Latest versions are available on the [project page](https://github.com
- `matinfo`, Displays information about materials compiled with `matc`
- `mipgen`, Generates a series of miplevels from a source image.
- `normal-blending`, Tool to blend normal maps
- `roughness-prefilter`, Pre-filters a roughness map from a normal map to reduce aliasing
- `roughness-prefilter`, Pre-filters a roughness map from a normal map to reduce aliasing
- `skygen`, Physically-based sky environment texture generator
- `specular-color`, Computes the specular color of conductors based on spectral data
@@ -29,10 +29,119 @@ Filament is distributed as a set of static libraries you must link against:
- `utils`, Support library for Filament
To use Filament from Java you must use the following two libraries instead:
- `filament-java.jar`, Contains Filament's Java classes
- `filament-java.jar`, Contains Filament's Java classes
- `filament-jni`, Filament's JNI bindings
To use the Vulkan backend on macOS you must also make the following libraries available at runtime:
- `MoltenVK_icd.json`
- `libMoltenVK.dylib`
- `libMoltenVK.dylib`
- `vulkan.1.dylib`
## Linking against Filament
This walkthrough will get you successfully compiling and linking native code
against Filament with minimum dependencies.
To start, download Filament's [latest binary release](https://github.com/google/filament/releases)
and extract into a directory of your choosing. Binary releases are suffixed
with the platform name, for example, `filament-20181009-linux.tgz`.
Create a file, `main.cpp`, in the same directory with the following contents:
```
#include <filament/FilamentAPI.h>
#include <filament/Engine.h>
using namespace filament;
int main(int argc, char** argv)
{
Engine *engine = Engine::create();
engine->destroy(&engine);
return 0;
}
```
The directory should look like:
```
|-- README.md
|-- bin
|-- docs
|-- include
|-- lib
|-- main.cpp
```
We'll use a platform-specific Makefile to compile and link `main.cpp` with Filament's libraries.
Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux
```
FILAMENT_LIBS=-lfilament -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils
CC=clang++
main: main.o
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -lpthread -lc++ -ldl -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++14 -pthread -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
```
### macOS
```
FILAMENT_LIBS=-lfilament -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils
CC=clang++
main: main.o
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -framework Cocoa -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++14 -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
```
### Windows
```
FILAMENT_LIBS=lib/x86_64/filament.lib lib/x86_64/bluegl.lib \
lib/x86_64/filabridge.lib lib/x86_64/filaflat.lib lib/x86_64/utils.lib
CC=clang-cl.exe
main.exe: main.obj
$(CC) main.obj $(FILAMENT_LIBS) gdi32.lib user32.lib opengl32.lib
main.obj: main.cpp
$(CC) /Iinclude/ /std:c++14 /c main.cpp
clean:
del main.exe main.obj
.PHONY: clean
```
### Compiling
You should be able to invoke `make` and run the executable successfully:
```
$ make
$ ./main
FEngine (64 bits) created at 0x106471000 (threading is enabled)
```
On Windows, you'll need to open up a Visual Studio Native Tools Command Prompt
and invoke `nmake` instead of `make`.

View File

@@ -0,0 +1,13 @@
## Versioning
Filament uses a 3-number versioning scheme that superficially resembles a [semantic
version](https://semver.org/) but is actually more interesting because of our material system. Here
are the guidelines:
- Increment the **most significant** number only when making a non-backwards compatible API change,
or when introducing a major new API.
- Increment the **middle number** only when making a non-backwards compatible change to the material
system. When this number gets bumped, users need to rebuild their mat files. Reset the middle
number to zero when the most significant number has been incremented.
- Increment the **least significant** number each time a new release is published. Reset this number
to zero if one of the other two numbers have been incremented.

View File

@@ -64,9 +64,9 @@ public:
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occured.
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occured, such as running out of
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/

View File

@@ -190,7 +190,7 @@ public:
* @param engine Reference to the filament::Engine to associate this IndirectLight with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occured.
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occured, such as running out of
* memory or other resources.

View File

@@ -421,7 +421,7 @@ public:
* @error if exceptions are disabled and an error occurs, this function is a no-op.
* Success can be checked by looking at the return value.
*
* @exception utils::PostConditionPanic if a runtime error occured, such as running out of
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/

View File

@@ -97,7 +97,7 @@ public:
* @param engine Reference to the filament::Engine to associate this Material with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occured.
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.

View File

@@ -86,7 +86,7 @@ public:
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param type Whether the color value is encoded as Linear or sRGB/A.
* @param color Array of read, green, blue and alpha chanels values.
* @param color Array of read, green, blue and alpha channels values.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, RgbaType type, math::float4 color) noexcept;

View File

@@ -181,7 +181,7 @@ public:
* @param flags One or more MirrorFrameFlag behavior configuration flags.
*
* @remark
* mirrorFrame() should be called after a frme is rendered using render()
* mirrorFrame() should be called after a frame is rendered using render()
* but before endFrame() is called.
*/
void mirrorFrame(SwapChain* dstSwapChain, Viewport const& dstViewport, Viewport const& srcViewport,

View File

@@ -150,7 +150,7 @@ public:
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low adresses
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
@@ -160,7 +160,7 @@ public:
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high adresses
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* Typically readPixels() will be called after Renderer.beginFrame().
@@ -176,6 +176,15 @@ public:
*/
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
driver::PixelBufferDescriptor&& buffer) noexcept;
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*
* This value can change at any time.
*
* @return timestamp in nanosecond.
*/
int64_t getTimestamp() const noexcept;
};
} // namespace filament

View File

@@ -175,9 +175,9 @@ public:
* @param engine Reference to the filament::Engine to associate this Texture with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occured.
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occured, such as running out of
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/

View File

@@ -97,7 +97,7 @@ public:
* @param e An entity.
*
* @note If this transform had children, these are orphaned, which means their local
* transform becomes a world transform. Usually it's not sensical. It's recomanded to make
* transform becomes a world transform. Usually it's not sensical. It's recommended to make
* sure that a destroyed transform doesn't have have children.
*
* @see create()

View File

@@ -69,9 +69,9 @@ public:
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occured.
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occured, such as running out of
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/

View File

@@ -75,7 +75,7 @@ public:
*
* enabled: enable or disables dynamic resolution on a View
* homogeneousScaling: by default the system scales the major axis first. Set this to true
* to force homegeneous scaling.
* to force homogeneous scaling.
* scaleRate: rate at which the scale will change to reach the target frame rate
* This value can be computed as 1 / N, where N is the number of frames
* needed to reach 64% of the target scale factor.
@@ -368,7 +368,7 @@ public:
* 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 lighits are not expected to be visible.
* @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).
*

View File

@@ -66,6 +66,13 @@ public:
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t& flags) noexcept = 0;
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
virtual void createDefaultRenderTarget(uint32_t& framebuffer, uint32_t& colorbuffer,
uint32_t& depthbuffer) noexcept {
framebuffer = 0;
colorbuffer = 0;
depthbuffer = 0;
}
// Called to make the OpenGL context active on the calling thread.
virtual void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept = 0;
@@ -73,7 +80,7 @@ public:
// swap draw buffers (i.e. for double-buffered rendering).
virtual void commit(SwapChain* swapChain) noexcept = 0;
virtual void setPresentationTime(long time) noexcept = 0;
virtual void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept = 0;
virtual bool canCreateFence() noexcept { return false; }
virtual Fence* createFence() noexcept = 0;
@@ -90,7 +97,7 @@ public:
// detach destroys the texture associated to the stream
virtual void detach(Stream* stream) noexcept = 0;
virtual void updateTexImage(Stream* stream) noexcept = 0;
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept = 0;
// external texture storage
virtual ExternalTexture* createExternalTextureStorage() noexcept = 0;

View File

@@ -35,10 +35,9 @@
#include "PrecompiledMaterials.h"
#include <filament/Exposure.h>
#include <private/filament/SibGenerator.h>
#include <private/filament/UibGenerator.h>
#include <filament/Exposure.h>
#include <filaflat/MaterialParser.h>
#include <filaflat/ShaderBuilder.h>
@@ -113,21 +112,6 @@ FEngine* FEngine::create(Backend backend, Platform* platform, void* sharedGLCont
return instance;
}
UniformInterfaceBlock FEngine::PerViewUib::getUib() noexcept {
return UibGenerator::getPerViewUib();
}
UniformInterfaceBlock FEngine::PostProcessingUib::getUib() noexcept {
return UibGenerator::getPostProcessingUib();
}
SamplerInterfaceBlock FEngine::PerViewSib::getSib() noexcept {
return SibGenerator::getPerViewSib();
}
SamplerInterfaceBlock FEngine::PostProcessSib::getSib() noexcept {
return SibGenerator::getPostProcessSib();
}
// these must be static because only a pointer is copied to the render stream
static const half4 sFullScreenTriangleVertices[3] = {
@@ -445,9 +429,9 @@ Handle<HwProgram> FEngine::createPostProcessProgram(MaterialParser& parser,
.withSamplerBindings(pBindings)
.withVertexShader(CString(vShaderBuilder.getShader(), vShaderBuilder.size()))
.withFragmentShader(CString(fShaderBuilder.getShader(), fShaderBuilder.size()))
.addUniformBlock(BindingPoints::PER_VIEW, &UibGenerator::getPerViewUib())
.addUniformBlock(BindingPoints::POST_PROCESS, &UibGenerator::getPostProcessingUib())
.addSamplerBlock(BindingPoints::POST_PROCESS, &SibGenerator::getPostProcessSib());
.addUniformBlock(BindingPoints::PER_VIEW, &PerViewUib::getUib())
.addUniformBlock(BindingPoints::POST_PROCESS, &PostProcessingUib::getUib())
.addSamplerBlock(BindingPoints::POST_PROCESS, &PostProcessSib::getSib());
auto program = const_cast<DriverApi&>(mCommandStream).createProgram(std::move(pb));
assert(program);
return program;

View File

@@ -71,7 +71,6 @@ constexpr size_t FROXEL_BUFFER_HEIGHT = (FROXEL_BUFFER_ENTRY_COUNT_MAX + F
constexpr size_t RECORD_BUFFER_WIDTH_SHIFT = 5u;
constexpr size_t RECORD_BUFFER_WIDTH = 1u << RECORD_BUFFER_WIDTH_SHIFT;
constexpr size_t RECORD_BUFFER_WIDTH_MASK = RECORD_BUFFER_WIDTH - 1u;
constexpr size_t RECORD_BUFFER_HEIGHT = 2048;
constexpr size_t RECORD_BUFFER_ENTRY_COUNT = RECORD_BUFFER_WIDTH * RECORD_BUFFER_HEIGHT; // 64K
@@ -168,12 +167,12 @@ bool Froxelizer::prepare(
// froxel buffer (~32 KiB)
mFroxelBufferUser = {
driverApi.allocatePod<FroxelEntry>(FROXEL_BUFFER_ENTRY_COUNT_MAX, CACHELINE_SIZE),
driverApi.allocatePod<FroxelEntry>(FROXEL_BUFFER_ENTRY_COUNT_MAX),
FROXEL_BUFFER_ENTRY_COUNT_MAX };
// record buffer (~64 KiB)
mRecordBufferUser = {
driverApi.allocatePod<RecordBufferType>(RECORD_BUFFER_ENTRY_COUNT, CACHELINE_SIZE),
driverApi.allocatePod<RecordBufferType>(RECORD_BUFFER_ENTRY_COUNT),
RECORD_BUFFER_ENTRY_COUNT };
/*
@@ -749,12 +748,7 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
} while(records[i].lights == b.lights);
}
out_of_memory:
// froxel buffer is always fully invalidated
mFroxelBuffer.invalidate();
// needed record buffer size may change at each frame
mRecordsBuffer.invalidate(0, (offset + RECORD_BUFFER_WIDTH_MASK) >> RECORD_BUFFER_WIDTH_SHIFT);
;
}
static inline float2 project(mat4f const& p, float3 const& v) noexcept {

View File

@@ -1,59 +0,0 @@
/*
* Copyright (C) 2015 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 "details/GpuLightBuffer.h"
#include "details/Engine.h"
#include <filament/EngineEnums.h>
#include <private/filament/UibGenerator.h>
namespace filament {
using namespace driver;
namespace details {
GpuLightBuffer::GpuLightBuffer(FEngine& engine) noexcept
: mLightsUb(UibGenerator::getLightsUib()) {
DriverApi& driverApi = engine.getDriverApi();
mLightUbh = driverApi.createUniformBuffer(mLightsUb.getSize());
driverApi.bindUniformBuffer(BindingPoints::LIGHTS, mLightUbh);
}
GpuLightBuffer::~GpuLightBuffer() noexcept = default;
void GpuLightBuffer::terminate(FEngine& engine) {
DriverApi& driverApi = engine.getDriverApi();
driverApi.destroyUniformBuffer(mLightUbh);
}
void GpuLightBuffer::commit(FEngine& engine) noexcept {
if (UTILS_UNLIKELY(mLightsUb.isDirty())) {
commitSlow(engine);
}
engine.getDriverApi().bindUniformBuffer(BindingPoints::LIGHTS, mLightUbh);
}
void GpuLightBuffer::commitSlow(FEngine& engine) noexcept {
DriverApi& driver = engine.getDriverApi();
driver.updateUniformBuffer(mLightUbh, mLightsUb.toBufferDescriptor(driver));
mLightsUb.clean();
}
} // namespace details
} // namespace filament

View File

@@ -60,7 +60,8 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createIndexBuffer(
(driver::ElementType)builder->mIndexType,
uint32_t(builder->mIndexCount));
uint32_t(builder->mIndexCount),
driver::BufferUsage::STATIC);
}
void FIndexBuffer::terminate(FEngine& engine) {
@@ -75,7 +76,7 @@ void FIndexBuffer::setBuffer(FEngine& engine,
byteSize = uint32_t(buffer.size);
}
engine.getDriverApi().loadIndexBuffer(mHandle, std::move(buffer), byteOffset, byteSize);
engine.getDriverApi().updateIndexBuffer(mHandle, std::move(buffer), byteOffset, byteSize);
}
} // namespace details

View File

@@ -26,8 +26,8 @@
#include <private/filament/UibGenerator.h>
#include <private/filament/Variant.h>
#include <filament/SamplerInterfaceBlock.h>
#include <filament/UniformInterfaceBlock.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <filaflat/MaterialParser.h>

View File

@@ -40,7 +40,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
if (!material->getUniformInterfaceBlock().isEmpty()) {
mUniforms = UniformBuffer(upcast(material)->getDefaultInstance()->mUniforms);
mUbHandle = driver.createUniformBuffer(mUniforms.getSize());
mUbHandle = driver.createUniformBuffer(mUniforms.getSize(), driver::BufferUsage::DYNAMIC);
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
@@ -63,7 +63,7 @@ void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* ma
if (!material->getUniformInterfaceBlock().isEmpty()) {
mUniforms = UniformBuffer(material->getUniformInterfaceBlock());
mUbHandle = driver.createUniformBuffer(mUniforms.getSize());
mUbHandle = driver.createUniformBuffer(mUniforms.getSize(), driver::BufferUsage::STATIC);
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {

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