diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index 8522ded64b..f19d056541 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -73,6 +73,7 @@ target_link_libraries(filament-jni GLESv3 EGL android + jnigraphics ) option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF) diff --git a/android/filament-android/src/main/cpp/CallbackUtils.cpp b/android/filament-android/src/main/cpp/CallbackUtils.cpp index edb75628c4..cc0c6d259b 100644 --- a/android/filament-android/src/main/cpp/CallbackUtils.cpp +++ b/android/filament-android/src/main/cpp/CallbackUtils.cpp @@ -56,7 +56,8 @@ JniCallback::~JniCallback() { void JniCallback::invoke(void*, size_t, void* user) { JniCallback* data = reinterpret_cast(user); - delete data; + // don't call delete here, because we don't own the storage + data->~JniCallback(); } JniBufferCallback* JniBufferCallback::make(filament::Engine* engine, diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp index 8f74bc7f3a..969a8b5bb4 100644 --- a/android/filament-android/src/main/cpp/Texture.cpp +++ b/android/filament-android/src/main/cpp/Texture.cpp @@ -19,6 +19,10 @@ #include #include +#ifdef ANDROID +#include +#endif + #include #include #include @@ -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(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(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 diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java index 1eafbcbeba..2b4f2a5d77 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java @@ -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); diff --git a/android/filament-android/src/main/java/com/google/android/filament/TextureSampler.java b/android/filament-android/src/main/java/com/google/android/filament/TextureSampler.java index 4799e945ba..07a15f3230 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/TextureSampler.java +++ b/android/filament-android/src/main/java/com/google/android/filament/TextureSampler.java @@ -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) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/android/TextureHelper.java b/android/filament-android/src/main/java/com/google/android/filament/android/TextureHelper.java new file mode 100644 index 0000000000..0a100813b4 --- /dev/null +++ b/android/filament-android/src/main/java/com/google/android/filament/android/TextureHelper.java @@ -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); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java index 4124da9a22..28fe76f760 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java +++ b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java @@ -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; } diff --git a/android/samples/README.md b/android/samples/README.md index 5e97d2a2b9..5bc8d9f4f1 100644 --- a/android/samples/README.md +++ b/android/samples/README.md @@ -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 diff --git a/android/samples/textured-object/.gitignore b/android/samples/textured-object/.gitignore new file mode 100644 index 0000000000..2878ee3967 --- /dev/null +++ b/android/samples/textured-object/.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/textured-object/app/.gitignore b/android/samples/textured-object/app/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/android/samples/textured-object/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/samples/textured-object/app/build.gradle b/android/samples/textured-object/app/build.gradle new file mode 100644 index 0000000000..068e8d27f2 --- /dev/null +++ b/android/samples/textured-object/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/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' +} diff --git a/android/samples/textured-object/app/proguard-rules.pro b/android/samples/textured-object/app/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/android/samples/textured-object/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/textured-object/app/src/main/AndroidManifest.xml b/android/samples/textured-object/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..88f3268024 --- /dev/null +++ b/android/samples/textured-object/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/android/samples/textured-object/app/src/main/assets/models/shader_ball.filamesh b/android/samples/textured-object/app/src/main/assets/models/shader_ball.filamesh new file mode 100644 index 0000000000..5997627c45 Binary files /dev/null and b/android/samples/textured-object/app/src/main/assets/models/shader_ball.filamesh differ diff --git a/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/IblLoader.kt b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/IblLoader.kt new file mode 100644 index 0000000000..cfd6c9c9b3 --- /dev/null +++ b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/IblLoader.kt @@ -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 { + 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 { + 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 { + 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) +} diff --git a/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/Io.kt b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/Io.kt new file mode 100644 index 0000000000..ec14760fe2 --- /dev/null +++ b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/Io.kt @@ -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 +} diff --git a/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MainActivity.kt b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MainActivity.kt new file mode 100644 index 0000000000..8a4dc4e249 --- /dev/null +++ b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MainActivity.kt @@ -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() } + } + } +} diff --git a/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MeshLoader.kt b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MeshLoader.kt new file mode 100644 index 0000000000..a2fdfd1019 --- /dev/null +++ b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/MeshLoader.kt @@ -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, 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 { + 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 { + 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, + definedMaterials: List, + materials: Map): 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) } +} diff --git a/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/TextureLoader.kt b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/TextureLoader.kt new file mode 100644 index 0000000000..e551c19999 --- /dev/null +++ b/android/samples/textured-object/app/src/main/java/com/google/android/filament/textured/TextureLoader.kt @@ -0,0 +1,98 @@ +/* + * 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 +} + +// 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 internalFormat(type: TextureType) = when (type) { + TextureType.COLOR -> Texture.InternalFormat.SRGB8_A8 + TextureType.NORMAL -> Texture.InternalFormat.RGB8 + TextureType.DATA -> Texture.InternalFormat.RGBA8 +} + +// 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") +} diff --git a/android/samples/textured-object/app/src/main/materials/textured_pbr.mat b/android/samples/textured-object/app/src/main/materials/textured_pbr.mat new file mode 100644 index 0000000000..98fffe76c8 --- /dev/null +++ b/android/samples/textured-object/app/src/main/materials/textured_pbr.mat @@ -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; + } +} diff --git a/android/samples/textured-object/app/src/main/res/drawable-v24/floor_ao_roughness_metallic.png b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_ao_roughness_metallic.png new file mode 100644 index 0000000000..296561bfbf Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_ao_roughness_metallic.png differ diff --git a/android/samples/textured-object/app/src/main/res/drawable-v24/floor_basecolor.png b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_basecolor.png new file mode 100644 index 0000000000..6effee4b5e Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_basecolor.png differ diff --git a/android/samples/textured-object/app/src/main/res/drawable-v24/floor_normal.png b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_normal.png new file mode 100644 index 0000000000..2d1cbdcb45 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/drawable-v24/floor_normal.png differ diff --git a/android/samples/textured-object/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/samples/textured-object/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..b1517edf49 --- /dev/null +++ b/android/samples/textured-object/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/android/samples/textured-object/app/src/main/res/drawable/ic_launcher_background.xml b/android/samples/textured-object/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..88e31872fe --- /dev/null +++ b/android/samples/textured-object/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/samples/textured-object/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/samples/textured-object/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..6d5e5d094c --- /dev/null +++ b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/samples/textured-object/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..6d5e5d094c --- /dev/null +++ b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/samples/textured-object/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..a2f5908281 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/samples/textured-object/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ff10afd6e1 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/samples/textured-object/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dcd3cd8083 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/samples/textured-object/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8ca12fe024 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/samples/textured-object/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b824ebdd48 Binary files /dev/null and b/android/samples/textured-object/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/samples/textured-object/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/samples/textured-object/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/textured-object/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/samples/textured-object/app/src/main/res/values/colors.xml b/android/samples/textured-object/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..5a077b3a78 --- /dev/null +++ b/android/samples/textured-object/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/android/samples/textured-object/app/src/main/res/values/strings.xml b/android/samples/textured-object/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..4a58bcb85a --- /dev/null +++ b/android/samples/textured-object/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Textured Object + diff --git a/android/samples/textured-object/app/src/main/res/values/styles.xml b/android/samples/textured-object/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..a7a06158ff --- /dev/null +++ b/android/samples/textured-object/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android/samples/textured-object/build.gradle b/android/samples/textured-object/build.gradle new file mode 100644 index 0000000000..a3176b9e3a --- /dev/null +++ b/android/samples/textured-object/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.71' + 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 +} diff --git a/android/samples/textured-object/gradle.properties b/android/samples/textured-object/gradle.properties new file mode 100644 index 0000000000..743d692ce1 --- /dev/null +++ b/android/samples/textured-object/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/textured-object/gradle/wrapper/gradle-wrapper.jar b/android/samples/textured-object/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..7a3265ee94 Binary files /dev/null and b/android/samples/textured-object/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/samples/textured-object/gradle/wrapper/gradle-wrapper.properties b/android/samples/textured-object/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..d4da7d2692 --- /dev/null +++ b/android/samples/textured-object/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/textured-object/gradlew b/android/samples/textured-object/gradlew new file mode 100755 index 0000000000..cccdd3d517 --- /dev/null +++ b/android/samples/textured-object/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/textured-object/gradlew.bat b/android/samples/textured-object/gradlew.bat new file mode 100644 index 0000000000..e95643d6a2 --- /dev/null +++ b/android/samples/textured-object/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/textured-object/settings.gradle b/android/samples/textured-object/settings.gradle new file mode 100644 index 0000000000..466fef9302 --- /dev/null +++ b/android/samples/textured-object/settings.gradle @@ -0,0 +1,3 @@ +includeBuild '../../filament-android' + +include ':app'