diff --git a/android/build/filament-tasks.gradle b/android/build/filament-tasks.gradle index 4ee59b3e5a..e1b48704e5 100644 --- a/android/build/filament-tasks.gradle +++ b/android/build/filament-tasks.gradle @@ -17,15 +17,22 @@ if (project.hasProperty("filament_tools_dir")) { filamentToolsPath = file("$filament_tools_dir") } -def matc = ['/bin/matc.exe', '/bin/matc'] -def matcFullPath = matc.collect { path -> Paths.get(filamentToolsPath.absolutePath, path).toFile() } +List getBinaries(String name, File toolsPath) { + def tool = ["/bin/${name}.exe", "/bin/${name}"] + def toolFullPath = tool.collect { path -> Paths.get(toolsPath.absolutePath, path).toFile() } -// Ensure that at least one matc binary and Filament library is present -if (!matcFullPath.any { path -> file(path).exists() }) { - throw new StopActionException("No matc binary could be found in " + filamentToolsPath + - "/bin. Ensure Filament has been built/installed before building this app.") + // Ensure that at least one matc binary and Filament library is present + if (!toolFullPath.any { path -> file(path).exists() }) { + throw new StopActionException("No ${name} binary could be found in " + toolsPath + + "/bin. Ensure Filament has been built/installed before building this app.") + } + + return toolFullPath } +ext.matcFullPath = getBinaries('matc', filamentToolsPath) +ext.cmgenFullPath = getBinaries('cmgen', filamentToolsPath) +ext.filameshFullPath = getBinaries('filamesh', filamentToolsPath) class LogOutputStream extends ByteArrayOutputStream { private final Logger logger; @@ -54,7 +61,6 @@ class LogOutputStream extends ByteArrayOutputStream { // Custom task to compile material files using matc // This task handles incremental builds class MaterialCompiler extends DefaultTask { - @Input File matcPath @SuppressWarnings("GroovyUnusedDeclaration") @@ -64,6 +70,11 @@ class MaterialCompiler extends DefaultTask { @OutputDirectory File outputDir + MaterialCompiler() { + matcPath = OperatingSystem.current().isWindows() ? + project.ext.matcFullPath[0] : project.ext.matcFullPath[1] + } + @SuppressWarnings("GroovyUnusedDeclaration") @TaskAction void execute(IncrementalTaskInputs inputs) { @@ -99,11 +110,119 @@ class MaterialCompiler extends DefaultTask { } } -task compileMaterials(type: MaterialCompiler) { - group 'Filament' - description 'Compile materials' +// Custom task to process IBLs using cmgen +// This task handles incremental builds +class IblGenerator extends DefaultTask { + File cmgenPath - inputDir = file("src/main/materials") - outputDir = file("src/main/assets/materials") - matcPath = OperatingSystem.current().isWindows() ? matcFullPath[0] : matcFullPath[1] + @SuppressWarnings("GroovyUnusedDeclaration") + @InputFile + File inputFile + + @OutputDirectory + File outputDir + + IblGenerator() { + cmgenPath = OperatingSystem.current().isWindows() ? + project.ext.cmgenFullPath[0] : project.ext.cmgenFullPath[1] + } + + @SuppressWarnings("GroovyUnusedDeclaration") + @TaskAction + void execute(IncrementalTaskInputs inputs) { + if (!inputs.incremental) { + project.delete(project.fileTree(outputDir).matching { include '*' }) + } + + inputs.outOfDate { InputFileDetails outOfDate -> + def file = outOfDate.file + + def out = new LogOutputStream(logger, LogLevel.INFO) + def err = new LogOutputStream(logger, LogLevel.ERROR) + + def header = ("Generating IBL " + file + "\n").getBytes() + err.write(header) + out.write(header) + + project.exec { + standardOutput out + errorOutput err + executable "${cmgenPath}" + args('-x', outputDir, file) + } + + project.exec { + standardOutput out + errorOutput err + executable "${cmgenPath}" + args('--format=rgbm', '--extract-blur=0.08', "--extract=${outputDir.absolutePath}", file) + } + } + + inputs.removed { InputFileDetails removed -> + getOutputFile(removed.file).delete() + } + } + + File getOutputFile(final File file) { + return new File(outputDir, file.name[0..file.name.lastIndexOf('.') - 1]) + } } + +// Custom task to compile mesh files using filamesh +// This task handles incremental builds +class MeshCompiler extends DefaultTask { + File filameshPath + + @SuppressWarnings("GroovyUnusedDeclaration") + @InputFile + File inputFile + + @OutputDirectory + File outputDir + + MeshCompiler() { + filameshPath = OperatingSystem.current().isWindows() ? + project.ext.filameshFullPath[0] : project.ext.filameshFullPath[1] + } + + @SuppressWarnings("GroovyUnusedDeclaration") + @TaskAction + void execute(IncrementalTaskInputs inputs) { + if (!inputs.incremental) { + project.delete(project.fileTree(outputDir).matching { include '*.filamesh' }) + } + + inputs.outOfDate { InputFileDetails outOfDate -> + def file = outOfDate.file + + def out = new LogOutputStream(logger, LogLevel.INFO) + def err = new LogOutputStream(logger, LogLevel.ERROR) + + def header = ("Compiling mesh " + file + "\n").getBytes() + err.write(header) + out.write(header) + + project.exec { + standardOutput out + errorOutput err + executable "${filameshPath}" + args(file, getOutputFile(file)) + } + } + + inputs.removed { InputFileDetails removed -> + getOutputFile(removed.file).delete() + } + } + + File getOutputFile(final File file) { + return new File(outputDir, file.name[0..file.name.lastIndexOf('.')] + 'filamesh') + } +} + +task compileMaterials(type: MaterialCompiler) + +task generateIbl(type: IblGenerator) + +task compileMesh(type: MeshCompiler) diff --git a/android/filament-android/src/main/cpp/TransformManager.cpp b/android/filament-android/src/main/cpp/TransformManager.cpp index 6d95cbc334..cfe83935c3 100644 --- a/android/filament-android/src/main/cpp/TransformManager.cpp +++ b/android/filament-android/src/main/cpp/TransformManager.cpp @@ -26,60 +26,68 @@ using namespace filament; static_assert(sizeof(jint) == sizeof(Entity), "jint and Entity are not compatible!!"); extern "C" JNIEXPORT jboolean JNICALL -Java_com_google_android_filament_TransformManager_nHasComponent(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint entity) { - TransformManager *tm = (TransformManager *) nativeTransformManager; - return (jboolean) tm->hasComponent((Entity &) entity); +Java_com_google_android_filament_TransformManager_nHasComponent(JNIEnv*, jclass, + jlong nativeTransformManager, jint entity_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); + return (jboolean) tm->hasComponent(entity); } extern "C" JNIEXPORT jint JNICALL -Java_com_google_android_filament_TransformManager_nGetInstance(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint entity) { - TransformManager *tm = (TransformManager *) nativeTransformManager; - return tm->getInstance((Entity &) entity); +Java_com_google_android_filament_TransformManager_nGetInstance(JNIEnv*, jclass, + jlong nativeTransformManager, jint entity_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); + return tm->getInstance(entity); } extern "C" JNIEXPORT jint JNICALL -Java_com_google_android_filament_TransformManager_nCreate__JI(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint entity) { - TransformManager *tm = (TransformManager *) nativeTransformManager; - tm->create((Entity &) entity); - return tm->getInstance((Entity &) entity); +Java_com_google_android_filament_TransformManager_nCreate(JNIEnv*, jclass, + jlong nativeTransformManager, jint entity_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); + tm->create(entity); + return tm->getInstance(entity); } extern "C" JNIEXPORT jint JNICALL -Java_com_google_android_filament_TransformManager_nCreate__JII_3F(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint entity, jint parent, jfloatArray localTransform_) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nCreateArray(JNIEnv* env, + jclass, jlong nativeTransformManager, jint entity_, jint parent, + jfloatArray localTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); if (localTransform_) { jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL); - tm->create((Entity &) entity, (TransformManager::Instance) parent, + tm->create(entity, (TransformManager::Instance) parent, *reinterpret_cast(localTransform)); env->ReleaseFloatArrayElements(localTransform_, localTransform, JNI_ABORT); } else { - tm->create((Entity &) entity, (TransformManager::Instance) parent); + tm->create(entity, (TransformManager::Instance) parent); } - return tm->getInstance((Entity &) entity); + return tm->getInstance(entity); } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nDestroy(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint entity) { - TransformManager *tm = (TransformManager *) nativeTransformManager; - tm->destroy((Entity &) entity); +Java_com_google_android_filament_TransformManager_nDestroy(JNIEnv*, jclass, + jlong nativeTransformManager, jint entity_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + Entity& entity = *reinterpret_cast(&entity_); + tm->destroy(entity); } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nSetParent(JNIEnv *env, jclass type, +Java_com_google_android_filament_TransformManager_nSetParent(JNIEnv*, jclass, jlong nativeTransformManager, jint i, jint newParent) { - TransformManager *tm = (TransformManager *) nativeTransformManager; - tm->setParent((TransformManager::Instance) i, (TransformManager::Instance) newParent); + TransformManager* tm = (TransformManager*) nativeTransformManager; + tm->setParent((TransformManager::Instance) i, + (TransformManager::Instance) newParent); } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint i, jfloatArray localTransform_) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jfloatArray localTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; jfloat *localTransform = env->GetFloatArrayElements(localTransform_, NULL); tm->setTransform((TransformManager::Instance) i, *reinterpret_cast(localTransform)); @@ -87,9 +95,10 @@ Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv *env, jcl } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nGetTransform(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint i, jfloatArray outLocalTransform_) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nGetTransform(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jfloatArray outLocalTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; jfloat *outLocalTransform = env->GetFloatArrayElements(outLocalTransform_, NULL); *reinterpret_cast(outLocalTransform) = tm->getTransform( (TransformManager::Instance) i); @@ -97,9 +106,10 @@ Java_com_google_android_filament_TransformManager_nGetTransform(JNIEnv *env, jcl } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nGetWorldTransform(JNIEnv *env, jclass type, - jlong nativeTransformManager, jint i, jfloatArray outWorldTransform_) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nGetWorldTransform(JNIEnv* env, + jclass, jlong nativeTransformManager, jint i, + jfloatArray outWorldTransform_) { + TransformManager* tm = (TransformManager*) nativeTransformManager; jfloat *outWorldTransform = env->GetFloatArrayElements(outWorldTransform_, NULL); *reinterpret_cast(outWorldTransform) = tm->getWorldTransform( (TransformManager::Instance) i); @@ -107,15 +117,15 @@ Java_com_google_android_filament_TransformManager_nGetWorldTransform(JNIEnv *env } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nOpenLocalTransformTransaction(JNIEnv *env, - jclass type, jlong nativeTransformManager) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nOpenLocalTransformTransaction( + JNIEnv*, jclass, jlong nativeTransformManager) { + TransformManager* tm = (TransformManager*) nativeTransformManager; tm->openLocalTransformTransaction(); } extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_TransformManager_nCommitLocalTransformTransaction(JNIEnv *env, - jclass type, jlong nativeTransformManager) { - TransformManager *tm = (TransformManager *) nativeTransformManager; +Java_com_google_android_filament_TransformManager_nCommitLocalTransformTransaction( + JNIEnv*, jclass, jlong nativeTransformManager) { + TransformManager* tm = (TransformManager*) nativeTransformManager; tm->commitLocalTransformTransaction(); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java index d6ced23553..6d335bbfe3 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java @@ -42,8 +42,9 @@ public class TransformManager { } @EntityInstance - public int create(@Entity int entity, @EntityInstance int parent, @Nullable @Size(min = 16) float[] localTransform) { - return nCreate(mNativeObject, entity, parent, localTransform); + public int create(@Entity int entity, @EntityInstance int parent, + @Nullable @Size(min = 16) float[] localTransform) { + return nCreateArray(mNativeObject, entity, parent, localTransform); } public void destroy(@Entity int entity) { @@ -54,14 +55,18 @@ public class TransformManager { nSetParent(mNativeObject, i, newParent); } - public void setTransform(@EntityInstance int i, @NonNull @Size(min = 16) float[] localTransform) { - if (localTransform.length < 16) throw new ArrayIndexOutOfBoundsException("Array length must be at least 16"); + public void setTransform(@EntityInstance int i, + @NonNull @Size(min = 16) float[] localTransform) { + if (localTransform.length < 16) { + throw new ArrayIndexOutOfBoundsException("Array length must be at least 16"); + } nSetTransform(mNativeObject, i, localTransform); } @NonNull @Size(min = 16) - public float[] getTransform(@EntityInstance int i, @Nullable @Size(min = 16) float[] outLocalTransform) { + public float[] getTransform(@EntityInstance int i, + @Nullable @Size(min = 16) float[] outLocalTransform) { outLocalTransform = assertMat4f(outLocalTransform); nGetTransform(mNativeObject, i, outLocalTransform); return outLocalTransform; @@ -69,7 +74,8 @@ public class TransformManager { @NonNull @Size(min = 16) - public float[] getWorldTransform(@EntityInstance int i, @Nullable @Size(min = 16) float[] outWorldTransform) { + public float[] getWorldTransform(@EntityInstance int i, + @Nullable @Size(min = 16) float[] outWorldTransform) { outWorldTransform = assertMat4f(outWorldTransform); nGetWorldTransform(mNativeObject, i, outWorldTransform); return outWorldTransform; @@ -95,7 +101,7 @@ public class TransformManager { private static native boolean nHasComponent(long nativeTransformManager, int entity); private static native int nGetInstance(long nativeTransformManager, int entity); private static native int nCreate(long nativeTransformManager, int entity); - private static native int nCreate(long mNativeObject, int entity, int parent, float[] localTransform); + private static native int nCreateArray(long mNativeObject, int entity, int parent, float[] localTransform); private static native void nDestroy(long nativeTransformManager, int entity); private static native void nSetParent(long nativeTransformManager, int i, int newParent); private static native void nSetTransform(long nativeTransformManager, int i, float[] localTransform); diff --git a/android/samples/README.md b/android/samples/README.md index 3ebac59244..3a994d6fd3 100644 --- a/android/samples/README.md +++ b/android/samples/README.md @@ -3,8 +3,9 @@ This directory contains several sample Android applications that demonstrate how to use the 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 +- `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 ## Prerequisites diff --git a/android/samples/hello-triangle/app/build.gradle b/android/samples/hello-triangle/app/build.gradle index 2278015bce..27596c6c0a 100644 --- a/android/samples/hello-triangle/app/build.gradle +++ b/android/samples/hello-triangle/app/build.gradle @@ -4,6 +4,14 @@ apply plugin: 'kotlin-android-extensions' apply from: '../../../build/filament-tasks.gradle' +compileMaterials { + group 'Filament' + description 'Compile materials' + + inputDir = file("src/main/materials") + outputDir = file("src/main/assets/materials") +} + preBuild.dependsOn compileMaterials android { diff --git a/android/samples/image-based-lighting/.gitignore b/android/samples/image-based-lighting/.gitignore new file mode 100644 index 0000000000..2878ee3967 --- /dev/null +++ b/android/samples/image-based-lighting/.gitignore @@ -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 diff --git a/android/samples/image-based-lighting/app/.gitignore b/android/samples/image-based-lighting/app/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/android/samples/image-based-lighting/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/samples/image-based-lighting/app/build.gradle b/android/samples/image-based-lighting/app/build.gradle new file mode 100644 index 0000000000..8ac1c8d2d8 --- /dev/null +++ b/android/samples/image-based-lighting/app/build.gradle @@ -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/flower_road_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.ibl" + minSdkVersion 21 + 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' +} diff --git a/android/samples/image-based-lighting/app/proguard-rules.pro b/android/samples/image-based-lighting/app/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/android/samples/image-based-lighting/app/proguard-rules.pro @@ -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 diff --git a/android/samples/image-based-lighting/app/src/main/AndroidManifest.xml b/android/samples/image-based-lighting/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..10e681ee39 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/android/samples/image-based-lighting/app/src/main/assets/models/shader_ball.filamesh b/android/samples/image-based-lighting/app/src/main/assets/models/shader_ball.filamesh new file mode 100644 index 0000000000..5997627c45 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/assets/models/shader_ball.filamesh differ diff --git a/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IblLoader.kt b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IblLoader.kt new file mode 100644 index 0000000000..8bbf643495 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IblLoader.kt @@ -0,0 +1,121 @@ +/* + * 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.ibl + +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 + +private fun peekSize(assets: AssetManager, name: String): Pair { + 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 +} + +fun loadIbl(assets: AssetManager, name: String, engine: Engine): IndirectLight { + 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.RGBM) + .sampler(Texture.Sampler.SAMPLER_CUBEMAP) + .build(engine) + + (0 until texture.levels).forEach { + 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) +} + +private fun loadSphericalHarmonics(assets: AssetManager, name: String): FloatArray { + // 3 bands = 9 RGB coefficients, so 9 * 3 floats + val sphericalHarmonics = FloatArray(9 * 3) + val input = BufferedReader(InputStreamReader(assets.open("$name/sh.txt"))) + val re = Regex("""\(\s*([+-]?\d+\.\d+),\s*([+-]?\d+\.\d+),\s*([+-]?\d+\.\d+)\);""") + (0 until 9).forEach { 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 + } + } + input.close() + return sphericalHarmonics +} + +fun loadSkybox(assets: AssetManager, name: String, engine: Engine): Skybox { + val (w, h) = peekSize(assets, "$name/nx.rgbm") + val texture = Texture.Builder() + .width(w) + .height(h) + .levels(1) + .format(Texture.InternalFormat.RGBM) + .sampler(Texture.Sampler.SAMPLER_CUBEMAP) + .build(engine) + + loadCubemap(texture, assets, name, engine) + + return Skybox.Builder().environment(texture).build(engine) +} + +private fun loadCubemap(texture: Texture, assets: AssetManager, name: String, + engine: Engine, prefix: String = "", level: Int = 0) { + + // RGBM is always 4 bytes per pixel + val faceSize = texture.getWidth(level) * texture.getHeight(level) * 4 + val offsets = IntArray(6) { it * faceSize } + + val opts = BitmapFactory.Options().apply { inPremultiplied = false } + + val storage = ByteBuffer.allocateDirect(faceSize * 6) + + val suffix = arrayOf("px", "nx", "py", "ny", "pz", "nz") + (0 until 6).forEach { + val input = assets.open("$name/$prefix${suffix[it]}.rgbm") + val bitmap = BitmapFactory.decodeStream(input, null, opts) + input.close() + + 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) +} diff --git a/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IoUtils.kt b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IoUtils.kt new file mode 100644 index 0000000000..c548480b8b --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/IoUtils.kt @@ -0,0 +1,52 @@ +/* + * 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.ibl + +import java.io.IOException +import java.io.InputStream +import java.nio.ByteBuffer +import java.nio.ByteOrder + +internal object IoUtils { + fun safelyClose(input: InputStream?) { + try { + input?.close() + } catch (e: IOException) { + // Ignore + } + } + + @Throws(IOException::class) + 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) + } + + @Throws(IOException::class) + fun readFloat32LE(input: InputStream): Float { + val bytes = ByteArray(4) + input.read(bytes, 0, 4) + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).float + } + + @Throws(IOException::class) + fun readUIntLE(input: InputStream): Long { + return readIntLE(input).toLong() and 0xFFFFFFFFL + } +} diff --git a/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MainActivity.kt b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MainActivity.kt new file mode 100644 index 0000000000..30dbe68fcb --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MainActivity.kt @@ -0,0 +1,332 @@ +/* + * 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.ibl + +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.io.IOException +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 mesh: Mesh + private lateinit var skybox: Skybox + private lateinit var indirectLight: IndirectLight + + // 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 + } + + private fun setupScene() { + loadMaterial() + setupMaterial() + loadImageBasedLight() + loadSkybox() + + scene.setSkybox(skybox) + scene.setIndirectLight(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/clear_coat.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() + // Specify that our color is in sRGB so the conversion to linear + // is done automatically for us. If you already have a linear color + // you can pass it directly, or use Colors.RgbType.LINEAR + materialInstance.setParameter("baseColor", Colors.RgbType.SRGB, 0.71f, 0.0f, 0.0f) + } + + private fun loadImageBasedLight() { + indirectLight = loadIbl(assets, "envs/flower_road_2k", engine) + indirectLight.intensity = 40_000.0f + } + + private fun loadSkybox() { + skybox = loadSkybox(assets, "envs/flower_road_2k", engine) + } + + 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 + engine.destroyEntity(light) + engine.destroyEntity(mesh.renderable) + engine.destroyRenderer(renderer) + engine.destroyVertexBuffer(mesh.vertexBuffer) + engine.destroyIndexBuffer(mesh.indexBuffer) + engine.destroyMaterialInstance(materialInstance) + engine.destroyMaterial(material) + engine.destroySkybox(skybox) + engine.destroyIndirectLight(indirectLight) + engine.destroyView(view) + engine.destroyScene(scene) + engine.destroyCamera(camera) + + // Engine.destroyEntity() destroys Filament related resources only + // (components), not the entity itself + val entityManager = EntityManager.get() + entityManager.destroy(light) + entityManager.destroy(mesh.renderable) + + // Destroying the engine will free up any resource you may have forgotten + // to destroy, but it's recommended to do the cleanup properly + engine.destroy() + } + + inner class FrameCallback : Choreographer.FrameCallback { + override fun doFrame(frameTimeNanos: Long) { + // Schedule the next frame + choreographer.postFrameCallback(this) + + // This check guarantees that we have a swap chain + if (uiHelper.isReadyToRender) { + // If beginFrame() returns false you should skip the frame + // This means you are sending frames too quickly to the GPU + if (renderer.beginFrame(swapChain!!)) { + renderer.render(view) + renderer.endFrame() + } + } + } + } + + inner class SurfaceCallback : UiHelper.RendererCallback { + override fun onNativeWindowChanged(surface: Surface) { + swapChain?.let { engine.destroySwapChain(it) } + swapChain = engine.createSwapChain(surface) + } + + override fun onDetachedFromSurface() { + swapChain?.let { + engine.destroySwapChain(it) + // Required to ensure we don't return before Filament is done executing the + // destroySwapChain command, otherwise Android might destroy the Surface + // too early + engine.flushAndWait() + swapChain = null + } + } + + override fun onResized(width: Int, height: Int) { + val aspect = width.toDouble() / height.toDouble() + camera.setProjection(45.0, aspect, 0.1, 20.0, Camera.Fov.VERTICAL) + + view.viewport = Viewport(0, 0, width, height) + } + } + + private fun readUncompressedAsset(assetName: String): ByteBuffer? { + var dst: ByteBuffer? = null + try { + assets.openFd(assetName).use { fd -> + val input = fd.createInputStream() + + dst = ByteBuffer.allocate(fd.length.toInt()) + + val src = Channels.newChannel(input) + src.read(dst) + src.close() + + dst!!.rewind() + } + } catch (e: IOException) { + e.printStackTrace() + } + + return dst + } +} diff --git a/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MeshLoader.kt b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MeshLoader.kt new file mode 100644 index 0000000000..14f4b30b12 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MeshLoader.kt @@ -0,0 +1,209 @@ +/* + * 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.ibl + +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 com.google.android.filament.ibl.IoUtils.readFloat32LE +import com.google.android.filament.ibl.IoUtils.readUIntLE + +import java.io.IOException +import java.io.InputStream +import java.nio.charset.Charset +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.channels.Channels + +const val FILAMESH_FILE_IDENTIFIER = "FILAMESH" +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() +} + +@Throws(IOException::class) +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 +} + +@Throws(IOException::class) +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 +} + +data class Mesh(@Entity val renderable: Int, + val vertexBuffer: VertexBuffer, val indexBuffer: IndexBuffer, val aabb: Box) + +fun loadMesh(assets: AssetManager, name: String, + materials: Map, engine: Engine): Mesh { + val input = assets.open(name) + val header = readHeader(input) + val channel = Channels.newChannel(input) + + val vertexBufferData = ByteBuffer.allocateDirect(header.verticesSizeInBytes.toInt()) + vertexBufferData.order(ByteOrder.LITTLE_ENDIAN) + channel.read(vertexBufferData) + vertexBufferData.flip() + + val indexBufferData = ByteBuffer.allocateDirect(header.indicesSizeInBytes.toInt()) + indexBufferData.order(ByteOrder.LITTLE_ENDIAN) + channel.read(indexBufferData) + indexBufferData.flip() + + val parts = 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 + } + + val definedMaterials = 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")) + } + + IoUtils.safelyClose(input) + + val indexType = if (header.indices16Bit != 0L) { + IndexBuffer.Builder.IndexType.USHORT + } else { + IndexBuffer.Builder.IndexType.UINT + } + + val indexBuffer = IndexBuffer.Builder() + .bufferType(indexType) + .indexCount(header.totalIndices.toInt()) + .build(engine) + indexBuffer.setBuffer(engine, indexBufferData) + + 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()) + .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()) + } + + val vertexBuffer = vertexBufferBuilder.build(engine) + vertexBuffer.setBufferAt(engine, 0, vertexBufferData) + + val builder = RenderableManager.Builder(header.parts.toInt()).boundingBox(header.aabb) + (0 until header.parts.toInt()).forEach { 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"]!!) + } + + val renderable = EntityManager.get().create() + builder.build(engine, renderable) + + return Mesh(renderable, vertexBuffer, indexBuffer, header.aabb) +} diff --git a/android/samples/image-based-lighting/app/src/main/materials/clear_coat.mat b/android/samples/image-based-lighting/app/src/main/materials/clear_coat.mat new file mode 100644 index 0000000000..077fd2774d --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/materials/clear_coat.mat @@ -0,0 +1,27 @@ +// Clear coat material with a single parameter: +// - baseColor + +material { + name : clear_coat, + shadingModel : lit, + parameters : [ + { + type : float3, + name : baseColor + } + ], +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + + material.baseColor.rgb = materialParams.baseColor; + + // To create a metallic paint-like material we want + // a rough base metallic layer and a glossy clear coat + material.roughness = 0.65; + material.metallic = 1.0; + material.clearCoat = 1.0; + } +} diff --git a/android/samples/image-based-lighting/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/samples/image-based-lighting/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..b1517edf49 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/android/samples/image-based-lighting/app/src/main/res/drawable/ic_launcher_background.xml b/android/samples/image-based-lighting/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..88e31872fe --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..6d5e5d094c --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..6d5e5d094c --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..a2f5908281 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..1b52399808 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ff10afd6e1 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..115a4c768a Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dcd3cd8083 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..459ca609d3 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8ca12fe024 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..8e19b410a1 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b824ebdd48 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..4c19a13c23 Binary files /dev/null and b/android/samples/image-based-lighting/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/samples/image-based-lighting/app/src/main/res/values/colors.xml b/android/samples/image-based-lighting/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..5a077b3a78 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/android/samples/image-based-lighting/app/src/main/res/values/strings.xml b/android/samples/image-based-lighting/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..a0c36da040 --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Image-Based Lighting + diff --git a/android/samples/image-based-lighting/app/src/main/res/values/styles.xml b/android/samples/image-based-lighting/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..a7a06158ff --- /dev/null +++ b/android/samples/image-based-lighting/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android/samples/image-based-lighting/build.gradle b/android/samples/image-based-lighting/build.gradle new file mode 100644 index 0000000000..51001877c4 --- /dev/null +++ b/android/samples/image-based-lighting/build.gradle @@ -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.2.60' + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.4' + 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 +} diff --git a/android/samples/image-based-lighting/gradle.properties b/android/samples/image-based-lighting/gradle.properties new file mode 100644 index 0000000000..743d692ce1 --- /dev/null +++ b/android/samples/image-based-lighting/gradle.properties @@ -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 diff --git a/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.jar b/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..7a3265ee94 Binary files /dev/null and b/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.properties b/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..d4da7d2692 --- /dev/null +++ b/android/samples/image-based-lighting/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/android/samples/image-based-lighting/gradlew b/android/samples/image-based-lighting/gradlew new file mode 100755 index 0000000000..cccdd3d517 --- /dev/null +++ b/android/samples/image-based-lighting/gradlew @@ -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" "$@" diff --git a/android/samples/image-based-lighting/gradlew.bat b/android/samples/image-based-lighting/gradlew.bat new file mode 100644 index 0000000000..e95643d6a2 --- /dev/null +++ b/android/samples/image-based-lighting/gradlew.bat @@ -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 diff --git a/android/samples/image-based-lighting/settings.gradle b/android/samples/image-based-lighting/settings.gradle new file mode 100644 index 0000000000..466fef9302 --- /dev/null +++ b/android/samples/image-based-lighting/settings.gradle @@ -0,0 +1,3 @@ +includeBuild '../../filament-android' + +include ':app' diff --git a/android/samples/lit-cube/app/build.gradle b/android/samples/lit-cube/app/build.gradle index 293364d567..223e4c8158 100644 --- a/android/samples/lit-cube/app/build.gradle +++ b/android/samples/lit-cube/app/build.gradle @@ -4,6 +4,14 @@ apply plugin: 'kotlin-android-extensions' apply from: '../../../build/filament-tasks.gradle' +compileMaterials { + group 'Filament' + description 'Compile materials' + + inputDir = file("src/main/materials") + outputDir = file("src/main/assets/materials") +} + preBuild.dependsOn compileMaterials android {