Introduce filament-utils and Java bindings for camutils.

This library houses the KTX loader, camutils bindings, and a Kotlin math
library. More will come later. Sample app is forthcoming.
This commit is contained in:
Philip Rideout
2020-01-07 17:12:45 -08:00
parent 6146b4d2d9
commit 961860adee
27 changed files with 2923 additions and 17 deletions

View File

@@ -31,3 +31,7 @@ jobs:
with:
name: gltfio-android-release
path: out/gltfio-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-utils-android-release
path: out/filament-utils-android-release.aar

View File

@@ -107,6 +107,7 @@ jobs:
mv out/filamat-android-full-release.aar out/filamat-${DATE}-full-android.aar
mv out/filamat-android-lite-release.aar out/filamat-${DATE}-lite-android.aar
mv out/gltfio-android-release.aar out/gltfio-${DATE}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${DATE}-android.aar
python3 build/common/upload-assets.py ${TAG} out/*.aar
env:
TAG: ${{ steps.git_ref.outputs.tag }}

View File

@@ -39,10 +39,11 @@ Here are all the libraries available in the group `com.google.android.filament`:
- `filament-android`: the Filament rendering engine itself
- `gltfio-android`: a glTF 2.0 loader for Filament, depends on `filament-android`
- `filament-utils-android`: KTX loading, Kotlin math, and camera utilities, depends on `gltfio-android`
- `filamat-android-full`: a runtime material builder/compiler. This library is large but contains
a full shader compiler/validator/optimizer
- `filamat-android-lite`: a much smaller alternative to `filamat-android-full` that can only
generate OpenGL shaders. It does not provide validation nor optimizations
generate OpenGL shaders. It does not provide validation or optimizations
### Snapshots

View File

@@ -0,0 +1,12 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/.idea/caches
/.idea/gradle.xml
.DS_Store
/build
/captures
.externalNativeBuild
/.cxx

View File

@@ -0,0 +1,104 @@
cmake_minimum_required(VERSION 3.6)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(DISABLE_GLTFIO_JNI TRUE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../gltfio-android ${CMAKE_CURRENT_BINARY_DIR}/gltfio-android)
add_library(filament STATIC IMPORTED)
set_target_properties(filament PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament.a)
add_library(backend STATIC IMPORTED)
set_target_properties(backend PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbackend.a)
add_library(camutils STATIC IMPORTED)
set_target_properties(camutils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libcamutils.a)
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
add_library(image STATIC IMPORTED)
set_target_properties(image PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libimage.a)
add_library(ibl STATIC IMPORTED)
set_target_properties(ibl PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl.a)
add_library(geometry STATIC IMPORTED)
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
add_library(filabridge STATIC IMPORTED)
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
add_library(gltfio STATIC IMPORTED)
set_target_properties(gltfio PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_core.a)
add_library(gltfio_resources STATIC IMPORTED)
set_target_properties(gltfio_resources PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_resources.a)
add_library(bluevk STATIC IMPORTED)
set_target_properties(bluevk PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbluevk.a)
add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
include_directories(${FILAMENT_DIR}/include
..
../../libs/utils/include)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
add_library(filament-utils-jni SHARED
src/main/cpp/Bookmark.cpp
src/main/cpp/Utils.cpp
src/main/cpp/Manipulator.cpp
../common/CallbackUtils.cpp
../common/NioUtils.cpp
$<TARGET_OBJECTS:gltfio-jni-obj>
$<TARGET_OBJECTS:filament-jni-obj>
)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.symbols)
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(filament-utils-jni
gltfio
filament
backend
filaflat
filabridge
camutils
geometry
image
ibl
utils
log
GLESv3
EGL
android
jnigraphics
gltfio_resources
m
)
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(filament-utils-jni bluevk smol-v)
endif()

View File

@@ -0,0 +1,63 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
buildToolsVersion versions.buildTools
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
externalNativeBuild {
cmake {
arguments.add("-DANDROID_PIE=ON")
arguments.add("-DANDROID_PLATFORM=android-${versions.targetSdk}".toString())
arguments.add("-DANDROID_STL=c++_static")
arguments.add("-DFILAMENT_DIST_DIR=${filamentPath}".toString())
cppFlags.add("-std=c++14")
if (project.hasProperty('extra_cmake_args')) {
arguments.add(extra_cmake_args)
}
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
sourceSets {
main {
jni.srcDirs "src/main/cpp"
kotlin.srcDirs += "src/main/java"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation deps.kotlin
implementation deps.androidx.annotations
implementation project(':filament-android')
implementation project(':gltfio-android')
}
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
afterEvaluate { project ->
publishing {
publications {
release(MavenPublication) {
artifactId = POM_ARTIFACT_ID
from components.release
}
}
}
}

View File

@@ -0,0 +1,3 @@
POM_NAME=Filament Android Utilities
POM_ARTIFACT_ID=filament-utils-android
POM_PACKAGING=aar

View File

@@ -0,0 +1,4 @@
LIBFILAMENT_UTILS {
global: Java_com_google_android_filament_*; JNI*;
local: *;
};

View File

@@ -0,0 +1 @@
_Java_com_google_android_filament_*

View File

@@ -0,0 +1,18 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.utils" />

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <camutils/Bookmark.h>
using namespace filament::camutils;
using namespace filament::math;
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Bookmark_nDestroyBookmark(JNIEnv*, jclass, jlong nativeBookmark) {
Bookmark<float>* bookmark = (Bookmark<float>*) nativeBookmark;
delete bookmark;
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <camutils/Manipulator.h>
using namespace filament::camutils;
using namespace filament::math;
using Builder = Manipulator<float>::Builder;
extern "C" JNIEXPORT jlong Java_com_google_android_filament_utils_Manipulator_nCreateBuilder(JNIEnv*, jclass) {
return (jlong) new Builder {};
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nDestroyBuilder(JNIEnv*, jclass, jlong nativeBuilder) {
Builder* builder = (Builder*) nativeBuilder;
delete builder;
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderViewport(JNIEnv*, jclass, jlong nativeBuilder, jint width, jint height) {
Builder* builder = (Builder*) nativeBuilder;
builder->viewport(width, height);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderTargetPosition(JNIEnv*, jclass, jlong nativeBuilder, jfloat x, jfloat y, jfloat z) {
Builder* builder = (Builder*) nativeBuilder;
builder->targetPosition(x, y, z);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderUpVector(JNIEnv*, jclass, jlong nativeBuilder, jfloat x, jfloat y, jfloat z) {
Builder* builder = (Builder*) nativeBuilder;
builder->upVector(x, y, z);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderZoomSpeed(JNIEnv*, jclass, jlong nativeBuilder, jfloat arg) {
Builder* builder = (Builder*) nativeBuilder;
builder->zoomSpeed(arg);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderOrbitHomePosition(JNIEnv*, jclass, jlong nativeBuilder, jfloat x, jfloat y, jfloat z) {
Builder* builder = (Builder*) nativeBuilder;
builder->orbitHomePosition(x, y, z);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderOrbitSpeed(JNIEnv*, jclass, jlong nativeBuilder, jfloat x, jfloat y) {
Builder* builder = (Builder*) nativeBuilder;
builder->orbitSpeed(x, y);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderFovDirection(JNIEnv*, jclass, jlong nativeBuilder, jint arg) {
Builder* builder = (Builder*) nativeBuilder;
builder->fovDirection((Fov) arg);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderFovDegrees(JNIEnv*, jclass, jlong nativeBuilder, jfloat arg) {
Builder* builder = (Builder*) nativeBuilder;
builder->fovDegrees(arg);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderFarPlane(JNIEnv*, jclass, jlong nativeBuilder, jfloat distance) {
Builder* builder = (Builder*) nativeBuilder;
builder->farPlane(distance);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderMapExtent(JNIEnv*, jclass, jlong nativeBuilder, jfloat width, jfloat height) {
Builder* builder = (Builder*) nativeBuilder;
builder->mapExtent(width, height);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderMapMinDistance(JNIEnv*, jclass, jlong nativeBuilder, jfloat arg) {
Builder* builder = (Builder*) nativeBuilder;
builder->mapMinDistance(arg);
}
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderGroundPlane(JNIEnv*, jclass, jlong nativeBuilder, jfloat a, jfloat b, jfloat c, jfloat d) {
Builder* builder = (Builder*) nativeBuilder;
builder->groundPlane(a, b, c, d);
}
extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) {
Builder* builder = (Builder*) nativeBuilder;
return (jlong) builder->build((Mode) mode);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_utils_Manipulator_nDestroyManipulator(JNIEnv*, jclass, jlong nativeManip) {
auto manip = (Manipulator<float>*) nativeManip;
delete manip;
}
extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_utils_Manipulator_nGetMode(JNIEnv*, jclass, jlong nativeManip) {
auto manip = (Manipulator<float>*) nativeManip;
return (int) manip->getMode();
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nSetViewport(JNIEnv*, jclass, jlong nativeManip, jint width, jint height) {
auto manip = (Manipulator<float>*) nativeManip;
manip->setViewport(width, height);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nGetLookAt(JNIEnv* env, jclass, jlong nativeManip, jfloatArray eyePosition, jfloatArray targetPosition, jfloatArray upward) {
auto manip = (Manipulator<float>*) nativeManip;
jfloat *eye = env->GetFloatArrayElements(eyePosition, NULL);
jfloat *target = env->GetFloatArrayElements(targetPosition, NULL);
jfloat *up = env->GetFloatArrayElements(upward, NULL);
manip->getLookAt((float3*) eye, (float3*) target, (float3*) up);
env->ReleaseFloatArrayElements(eyePosition, eye, 0);
env->ReleaseFloatArrayElements(targetPosition, target, 0);
env->ReleaseFloatArrayElements(upward, up, 0);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nRaycast(JNIEnv* env, jclass, jlong nativeManip, jint x, jint y, jfloatArray result) {
auto manip = (Manipulator<float>*) nativeManip;
jfloat *presult = env->GetFloatArrayElements(result, NULL);
manip->raycast(x, y, (float3*) presult);
env->ReleaseFloatArrayElements(result, presult, 0);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nGrabBegin(JNIEnv*, jclass, jlong nativeManip, jint x, jint y, jboolean strafe) {
auto manip = (Manipulator<float>*) nativeManip;
manip->grabBegin(x, y, (bool) strafe);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nGrabUpdate(JNIEnv*, jclass, jlong nativeManip, jint x, jint y) {
auto manip = (Manipulator<float>*) nativeManip;
manip->grabUpdate(x, y);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nGrabEnd(JNIEnv*, jclass, jlong nativeManip) {
auto manip = (Manipulator<float>*) nativeManip;
manip->grabEnd();
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nZoom(JNIEnv*, jclass, jlong nativeManip, jint x, jint y, jfloat scrolldelta) {
auto manip = (Manipulator<float>*) nativeManip;
manip->zoom(x, y, scrolldelta);
}
extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_utils_Manipulator_nGetCurrentBookmark(JNIEnv*, jclass, jlong nativeManip) {
auto manip = (Manipulator<float>*) nativeManip;
return (jlong) new Bookmark<float>(manip->getCurrentBookmark());
}
extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_utils_Manipulator_nGetHomeBookmark(JNIEnv*, jclass, jlong nativeManip) {
auto manip = (Manipulator<float>*) nativeManip;
return (jlong) new Bookmark<float>(manip->getHomeBookmark());
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_Manipulator_nJumpToBookmark(JNIEnv*, jclass, jlong nativeManip, jlong nativeBookmark) {
auto manip = (Manipulator<float>*) nativeManip;
auto bookmark = (Bookmark<float>*) nativeBookmark;
manip->jumpToBookmark(*bookmark);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* Copyright (C) 2020 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.
@@ -28,8 +28,23 @@ using namespace filament;
using namespace filament::math;
using namespace image;
extern void registerCallbackUtils(JNIEnv*);
extern void registerNioUtils(JNIEnv*);
jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
registerCallbackUtils(env);
registerNioUtils(env);
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_KtxLoader_nCreateTexture(JNIEnv* env, jclass,
Java_com_google_android_filament_utils_KtxLoader_nCreateTexture(JNIEnv* env, jclass,
jlong nativeEngine, jobject javaBuffer, jint remaining, jboolean srgb) {
Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, javaBuffer, remaining);
@@ -41,7 +56,7 @@ Java_com_google_android_filament_gltfio_KtxLoader_nCreateTexture(JNIEnv* env, jc
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_KtxLoader_nCreateIndirectLight(JNIEnv* env, jclass,
Java_com_google_android_filament_utils_KtxLoader_nCreateIndirectLight(JNIEnv* env, jclass,
jlong nativeEngine, jobject javaBuffer, jint remaining, jboolean srgb) {
Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, javaBuffer, remaining);
@@ -64,7 +79,7 @@ Java_com_google_android_filament_gltfio_KtxLoader_nCreateIndirectLight(JNIEnv* e
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_KtxLoader_nCreateSkybox(JNIEnv* env, jclass,
Java_com_google_android_filament_utils_KtxLoader_nCreateSkybox(JNIEnv* env, jclass,
jlong nativeEngine, jobject javaBuffer, jint remaining, jboolean srgb) {
Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, javaBuffer, remaining);

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 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.utils;
public class Bookmark {
private long mNativeObject;
Bookmark(long nativeObject) {
mNativeObject = nativeObject;
}
long getNativeObject() {
return mNativeObject;
}
@Override
protected void finalize() throws Throwable {
nDestroyBookmark(mNativeObject);
super.finalize();
}
private static native void nDestroyBookmark(long nativeObject);
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2020 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.utils
import android.view.MotionEvent
import android.view.View
import java.util.*
/**
* Sets up a touch listener on an Android View and forwards events to a camera manipulator.
* Supports one-touch orbit, two-touch pan, and pinch-to-zoom.
*/
class GestureDetector(private val view: View, private val manipulator: Manipulator) {
private enum class Gesture { NONE, ORBIT, PAN, ZOOM }
// Simplified memento of MotionEvent, minimal but sufficient for our purposes.
private data class TouchEvent(var pt0: Float2, var pt1: Float2, var count: Int) {
constructor() : this(Float2(0f), Float2(0f), 0)
constructor(me: MotionEvent) : this() {
if (me.pointerCount >= 1) {
this.pt0 = Float2(me.getX(0), me.getY(0))
this.count++
}
if (me.pointerCount >= 2) {
this.pt1 = Float2(me.getX(1), me.getY(1))
this.count++
}
}
fun getPinchDistance(): Float { return distance(pt0, pt1) }
fun getTouchesMidpoint(): Float2 { return mix(pt0, pt1, 0.5f) }
}
private var currentGesture = Gesture.NONE
private var previousEvent = TouchEvent()
private val tentativePanEvents = ArrayList<TouchEvent>()
private val tentativeOrbitEvents = ArrayList<TouchEvent>()
private val tentativeZoomEvents = ArrayList<TouchEvent>()
private val kGestureConfidenceCount = 2
private val kPanConfidenceDistance = 5
private val kZoomConfidenceDistance = 10
private val kZoomSpeed = 1f / 10f
init {
view.setOnTouchListener func@{ _, event ->
val x = event.getX(0).toInt()
val y = view.height - event.getY(0).toInt()
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> {
// CANCEL GESTURE DUE TO UNEXPECTED POINTER COUNT
if ((event.pointerCount != 1 && currentGesture == Gesture.ORBIT) ||
(event.pointerCount != 2 && currentGesture == Gesture.PAN) ||
(event.pointerCount != 2 && currentGesture == Gesture.ZOOM)) {
endGesture()
return@func true
}
// UPDATE EXISTING GESTURE
if (currentGesture == Gesture.ZOOM) {
val d0 = previousEvent.getPinchDistance()
val d1 = TouchEvent(event).getPinchDistance()
manipulator.zoom(x, y, (d0 - d1) * kZoomSpeed)
previousEvent = TouchEvent(event)
return@func true
}
if (currentGesture != Gesture.NONE) {
manipulator.grabUpdate(x, y)
return@func true
}
// DETECT NEW GESTURE
if (event.pointerCount == 1) {
tentativeOrbitEvents.add(TouchEvent(event))
}
if (event.pointerCount == 2) {
tentativePanEvents.add(TouchEvent(event))
tentativeZoomEvents.add(TouchEvent(event))
}
if (isOrbitGesture()) {
manipulator.grabBegin(x, y, false)
currentGesture = Gesture.ORBIT
return@func true
}
if (isZoomGesture()) {
currentGesture = Gesture.ZOOM
previousEvent = TouchEvent(event)
return@func true
}
if (isPanGesture()) {
manipulator.grabBegin(x, y, true)
currentGesture = Gesture.PAN
return@func true
}
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
endGesture()
}
}
true
}
}
private fun endGesture() {
tentativePanEvents.clear()
tentativeOrbitEvents.clear()
tentativeZoomEvents.clear()
currentGesture = Gesture.NONE
manipulator.grabEnd()
}
private fun isOrbitGesture(): Boolean {
return tentativeOrbitEvents.size > kGestureConfidenceCount
}
private fun isPanGesture(): Boolean {
if (tentativePanEvents.size <= kGestureConfidenceCount) {
return false
}
val oldest = tentativePanEvents.first().getTouchesMidpoint()
val newest = tentativePanEvents.last().getTouchesMidpoint()
return distance(oldest, newest) > kPanConfidenceDistance
}
private fun isZoomGesture(): Boolean {
if (tentativeZoomEvents.size <= kGestureConfidenceCount) {
return false
}
val oldest = tentativeZoomEvents.first().getPinchDistance()
val newest = tentativeZoomEvents.last().getPinchDistance()
return kotlin.math.abs(newest - oldest) > kZoomConfidenceDistance
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.android.filament.gltfio;
package com.google.android.filament.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -25,7 +25,6 @@ import com.google.android.filament.Skybox;
import com.google.android.filament.Texture;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.Buffer;
/**

View File

@@ -0,0 +1,369 @@
/*
* Copyright (C) 2020 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.utils;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
/**
* Helper that enables camera interaction similar to sketchfab or Google Maps.
*
* Clients notify the camera manipulator of various mouse or touch events, then periodically call
* its getLookAt() method so that they can adjust their camera(s). Two modes are supported: ORBIT
* and MAP. To construct a manipulator instance, the desired mode is passed into the create method.
*
* @see Bookmark
*/
public class Manipulator {
private long mNativeObject;
private Manipulator(long nativeIndexBuffer) {
mNativeObject = nativeIndexBuffer;
}
public enum Mode { ORBIT, MAP };
public enum Fov { VERTICAL, HORIZONTAL };
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
// Keep to finalize native resources
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Width and height of the viewing area.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder viewport(@IntRange(from = 1) int width, @IntRange(from = 1) int height) {
nBuilderViewport(mNativeBuilder, width, height);
return this;
}
/**
* Sets world-space position of interest, which defaults to (0,0,0).
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder targetPosition(float x, float y, float z) {
nBuilderTargetPosition(mNativeBuilder, x, y, z);
return this;
}
/**
* Sets orientation for the home position, which defaults to (0,1,0).
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder upVector(float x, float y, float z) {
nBuilderUpVector(mNativeBuilder, x, y, z);
return this;
}
/**
* Sets the scroll delta multiplier, which defaults to 0.01.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder zoomSpeed(float arg) {
nBuilderZoomSpeed(mNativeBuilder, arg);
return this;
}
/**
* Sets initial eye position in world space for ORBIT mode.
* This defaults to (0,0,1).
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder orbitHomePosition(float x, float y, float z) {
nBuilderOrbitHomePosition(mNativeBuilder, x, y, z);
return this;
}
/**
* Sets the multiplier with viewport delta for ORBIT mode.
* This defaults to 0.01
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder orbitSpeed(float x, float y) {
nBuilderOrbitSpeed(mNativeBuilder, x, y);
return this;
}
/**
* Sets the FOV axis that's held constant when the viewport changes.
* This defaults to Vertical.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder fovDirection(Fov fov) {
nBuilderFovDirection(mNativeBuilder, fov.ordinal());
return this;
}
/**
* Sets the full FOV (not the half-angle) in the degrees.
* This defaults to 33.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder fovDegrees(float arg) {
nBuilderFovDegrees(mNativeBuilder, arg);
return this;
}
/**
* Sets the distance to the far plane, which defaults to 5000.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder farPlane(float arg) {
nBuilderFarPlane(mNativeBuilder, arg);
return this;
}
/**
* Sets the ground plane size used to compute the home position for MAP mode.
* This defaults to 512 x 512
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder mapExtent(float width, float height) {
nBuilderMapExtent(mNativeBuilder, width, height);
return this;
}
/**
* Constrains the zoom-in level. Defaults to 0.
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder mapMinDistance(float arg) {
nBuilderMapMinDistance(mNativeBuilder, arg);
return this;
}
/**
* Sets the ground plane equation used for ray casts.
* This is a plane equation as in Ax + By + Cz + D = 0.
* Defaults to (0, 0, 1, 0).
*
* @return this <code>Builder</code> object for chaining calls
*/
@NonNull
public Builder groundPlane(float a, float b, float c, float d) {
nBuilderGroundPlane(mNativeBuilder, a, b, c, d);
return this;
}
/**
* Creates and returns the <code>Manipulator</code> object.
*
* @return the newly created <code>Manipulator</code> object
*
* @exception IllegalStateException if the Manipulator could not be created
*
*/
@NonNull
public Manipulator build(Mode mode) {
long nativeManipulator = nBuilderBuild(mNativeBuilder, mode.ordinal());
if (nativeManipulator == 0)
throw new IllegalStateException("Couldn't create Manipulator");
return new Manipulator(nativeManipulator);
}
private static class BuilderFinalizer {
private final long mNativeObject;
BuilderFinalizer(long nativeObject) {
mNativeObject = nativeObject;
}
@Override
public void finalize() {
try {
super.finalize();
} catch (Throwable t) { // Ignore
} finally {
nDestroyBuilder(mNativeObject);
}
}
}
};
@Override
public void finalize() {
try {
super.finalize();
} catch (Throwable t) { // Ignore
} finally {
nDestroyManipulator(mNativeObject);
}
}
/**
* Gets the immutable mode of the manipulator.
*/
public Mode getMode() { return Mode.values()[nGetMode(mNativeObject)]; }
/**
* Sets the viewport dimensions in terms of pixels.
*
* The manipulator uses this only in the grab and raycast methods, since
* those methods consume coordinates in viewport space.
*/
public void setViewport(int width, int height) {
nSetViewport(mNativeObject, width, height);
}
/**
* Gets the current orthonormal basis. This is usually called once per frame.
*/
public void getLookAt(
@NonNull @Size(min = 3) float[] eyePosition,
@NonNull @Size(min = 3) float[] targetPosition,
@NonNull @Size(min = 3) float[] upward) {
nGetLookAt(mNativeObject, eyePosition, targetPosition, upward);
}
/**
* Given a viewport coordinate, picks a point in the ground plane.
*/
@Nullable @Size(min = 3)
public float[] raycast(int x, int y) {
float[] result = new float[3];
nRaycast(mNativeObject, x, y, result);
return result;
}
/**
* Starts a grabbing session (i.e. the user is dragging around in the viewport).
*
* This starts a panning session in MAP mode, and start either rotating or strafing in ORBIT.
*
* @param x X-coordinate for point of interest in viewport space
* @param y Y-coordinate for point of interest in viewport space
* @param strafe ORBIT mode only; if true, starts a translation rather than a rotation.
*/
public void grabBegin(int x, int y, boolean strafe) {
nGrabBegin(mNativeObject, x, y, strafe);
}
/**
* Updates a grabbing session.
*
* This must be called at least once between grabBegin / grabEnd to dirty the camera.
*/
public void grabUpdate(int x, int y) {
nGrabUpdate(mNativeObject, x, y);
}
/**
* Ends a grabbing session.
*/
public void grabEnd() {
nGrabEnd(mNativeObject);
}
/**
* Dollys the camera along the viewing direction.
*
* @param x X-coordinate for point of interest in viewport space
* @param y Y-coordinate for point of interest in viewport space
* @param scrolldelta Positive means "zoom in", negative means "zoom out"
*/
public void zoom(int x, int y, float scrolldelta) {
nZoom(mNativeObject, x, y, scrolldelta);
}
/**
* Gets a handle that can be used to reset the manipulator back to its current position.
*
* \see jumpToBookmark
*/
public Bookmark getCurrentBookmark() {
return new Bookmark(nGetCurrentBookmark(mNativeObject));
}
/**
* Gets a handle that can be used to reset the manipulator back to its home position.
*
* see jumpToBookmark
*/
public Bookmark getHomeBookmark() {
return new Bookmark(nGetHomeBookmark(mNativeObject));
}
/**
* Sets the manipulator position and orientation back to a stashed state.
*
* \see getCurrentBookmark, getHomeBookmark
*/
public void jumpToBookmark(Bookmark bookmark) {
nJumpToBookmark(mNativeObject, bookmark.getNativeObject());
}
private static native long nCreateBuilder();
private static native void nDestroyBuilder(long nativeBuilder);
private static native void nBuilderViewport(long nativeBuilder, int width, int height);
private static native void nBuilderTargetPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderUpVector(long nativeBuilder, float x, float y, float z);
private static native void nBuilderZoomSpeed(long nativeBuilder, float arg);
private static native void nBuilderOrbitHomePosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderOrbitSpeed(long nativeBuilder, float x, float y);
private static native void nBuilderFovDirection(long nativeBuilder, int arg);
private static native void nBuilderFovDegrees(long nativeBuilder, float arg);
private static native void nBuilderFarPlane(long nativeBuilder, float distance);
private static native void nBuilderMapExtent(long nativeBuilder, float width, float height);
private static native void nBuilderMapMinDistance(long nativeBuilder, float arg);
private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d);
private static native long nBuilderBuild(long nativeBuilder, int mode);
private static native void nDestroyManipulator(long nativeManip);
private static native int nGetMode(long nativeManip);
private static native void nSetViewport(long nativeManip, int width, int height);
private static native void nGetLookAt(long nativeManip, float[] eyePosition, float[] targetPosition, float[] upward);
private static native void nRaycast(long nativeManip, int x, int y, float[] result);
private static native void nGrabBegin(long nativeManip, int x, int y, boolean strafe);
private static native void nGrabUpdate(long nativeManip, int x, int y);
private static native void nGrabEnd(long nativeManip);
private static native void nZoom(long nativeManip, int x, int y, float scrolldelta);
private static native long nGetCurrentBookmark(long nativeManip);
private static native long nGetHomeBookmark(long nativeManip);
private static native void nJumpToBookmark(long nativeManip, long nativeBookmark);
}

View File

@@ -0,0 +1,520 @@
/*
* Copyright (C) 2017 Romain Guy
*
* 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.utils
import kotlin.math.*
enum class MatrixColumn {
X, Y, Z, W
}
data class Mat2(
var x: Float2 = Float2(x = 1.0f),
var y: Float2 = Float2(y = 1.0f)) {
constructor(m: Mat2) : this(m.x.copy(), m.y.copy())
companion object {
fun of(vararg a: Float): Mat2 {
require(a.size >= 4)
return Mat2(
Float2(a[0], a[2]),
Float2(a[1], a[3])
)
}
fun identity() = Mat2()
}
operator fun get(column: Int) = when(column) {
0 -> x
1 -> y
else -> throw IllegalArgumentException("column must be in 0..1")
}
operator fun get(column: Int, row: Int) = get(column)[row]
operator fun get(column: MatrixColumn) = when(column) {
MatrixColumn.X -> x
MatrixColumn.Y -> y
else -> throw IllegalArgumentException("column must be X or Y")
}
operator fun get(column: MatrixColumn, row: Int) = get(column)[row]
operator fun invoke(row: Int, column: Int) = get(column - 1)[row - 1]
operator fun invoke(row: Int, column: Int, v: Float) = set(column - 1, row - 1, v)
operator fun set(column: Int, v: Float2) {
this[column].xy = v
}
operator fun set(column: Int, row: Int, v: Float) {
this[column][row] = v
}
operator fun unaryMinus() = Mat2(-x, -y)
operator fun inc(): Mat2 {
x++
y++
return this
}
operator fun dec(): Mat2 {
x--
y--
return this
}
operator fun plus(v: Float) = Mat2(x + v, y + v)
operator fun minus(v: Float) = Mat2(x - v, y - v)
operator fun times(v: Float) = Mat2(x * v, y * v)
operator fun div(v: Float) = Mat2(x / v, y / v)
operator fun times(m: Mat2): Mat2 {
val t = transpose(this)
return Mat2(
Float2(dot(t.x, m.x), dot(t.y, m.x)),
Float2(dot(t.x, m.y), dot(t.y, m.y))
)
}
operator fun times(v: Float2): Float2 {
val t = transpose(this)
return Float2(dot(t.x, v), dot(t.y, v))
}
fun toFloatArray() = floatArrayOf(
x.x, y.x,
x.y, y.y
)
override fun toString(): String {
return """
|${x.x} ${y.x}|
|${x.y} ${y.y}|
""".trimIndent()
}
}
data class Mat3(
var x: Float3 = Float3(x = 1.0f),
var y: Float3 = Float3(y = 1.0f),
var z: Float3 = Float3(z = 1.0f)) {
constructor(m: Mat3) : this(m.x.copy(), m.y.copy(), m.z.copy())
companion object {
fun of(vararg a: Float): Mat3 {
require(a.size >= 9)
return Mat3(
Float3(a[0], a[3], a[6]),
Float3(a[1], a[4], a[7]),
Float3(a[2], a[5], a[8])
)
}
fun identity() = Mat3()
}
operator fun get(column: Int) = when(column) {
0 -> x
1 -> y
2 -> z
else -> throw IllegalArgumentException("column must be in 0..2")
}
operator fun get(column: Int, row: Int) = get(column)[row]
operator fun get(column: MatrixColumn) = when(column) {
MatrixColumn.X -> x
MatrixColumn.Y -> y
MatrixColumn.Z -> z
else -> throw IllegalArgumentException("column must be X, Y or Z")
}
operator fun get(column: MatrixColumn, row: Int) = get(column)[row]
operator fun invoke(row: Int, column: Int) = get(column - 1)[row - 1]
operator fun invoke(row: Int, column: Int, v: Float) = set(column - 1, row - 1, v)
operator fun set(column: Int, v: Float3) {
this[column].xyz = v
}
operator fun set(column: Int, row: Int, v: Float) {
this[column][row] = v
}
operator fun unaryMinus() = Mat3(-x, -y, -z)
operator fun inc(): Mat3 {
x++
y++
z++
return this
}
operator fun dec(): Mat3 {
x--
y--
z--
return this
}
operator fun plus(v: Float) = Mat3(x + v, y + v, z + v)
operator fun minus(v: Float) = Mat3(x - v, y - v, z - v)
operator fun times(v: Float) = Mat3(x * v, y * v, z * v)
operator fun div(v: Float) = Mat3(x / v, y / v, z / v)
operator fun times(m: Mat3): Mat3 {
val t = transpose(this)
return Mat3(
Float3(dot(t.x, m.x), dot(t.y, m.x), dot(t.z, m.x)),
Float3(dot(t.x, m.y), dot(t.y, m.y), dot(t.z, m.y)),
Float3(dot(t.x, m.z), dot(t.y, m.z), dot(t.z, m.z))
)
}
operator fun times(v: Float3): Float3 {
val t = transpose(this)
return Float3(dot(t.x, v), dot(t.y, v), dot(t.z, v))
}
fun toFloatArray() = floatArrayOf(
x.x, y.x, z.x,
x.y, y.y, z.y,
x.z, y.z, z.z
)
override fun toString(): String {
return """
|${x.x} ${y.x} ${z.x}|
|${x.y} ${y.y} ${z.y}|
|${x.z} ${y.z} ${z.z}|
""".trimIndent()
}
}
data class Mat4(
var x: Float4 = Float4(x = 1.0f),
var y: Float4 = Float4(y = 1.0f),
var z: Float4 = Float4(z = 1.0f),
var w: Float4 = Float4(w = 1.0f)) {
constructor(right: Float3, up: Float3, forward: Float3, position: Float3 = Float3()) :
this(Float4(right), Float4(up), Float4(forward), Float4(position, 1.0f))
constructor(m: Mat4) : this(m.x.copy(), m.y.copy(), m.z.copy(), m.w.copy())
companion object {
fun of(vararg a: Float): Mat4 {
require(a.size >= 16)
return Mat4(
Float4(a[0], a[4], a[8], a[12]),
Float4(a[1], a[5], a[9], a[13]),
Float4(a[2], a[6], a[10], a[14]),
Float4(a[3], a[7], a[11], a[15])
)
}
fun identity() = Mat4()
}
inline var right: Float3
get() = x.xyz
set(value) {
x.xyz = value
}
inline var up: Float3
get() = y.xyz
set(value) {
y.xyz = value
}
inline var forward: Float3
get() = z.xyz
set(value) {
z.xyz = value
}
inline var position: Float3
get() = w.xyz
set(value) {
w.xyz = value
}
inline val scale: Float3
get() = Float3(length(x.xyz), length(y.xyz), length(z.xyz))
inline val translation: Float3
get() = w.xyz
val rotation: Float3
get() {
val x = normalize(right)
val y = normalize(up)
val z = normalize(forward)
return when {
z.y <= -1.0f -> Float3(degrees(-HALF_PI), 0.0f, degrees(atan2( x.z, y.z)))
z.y >= 1.0f -> Float3(degrees( HALF_PI), 0.0f, degrees(atan2(-x.z, -y.z)))
else -> Float3(
degrees(-asin(z.y)), degrees(-atan2(z.x, z.z)), degrees(atan2( x.y, y.y)))
}
}
inline val upperLeft: Mat3
get() = Mat3(x.xyz, y.xyz, z.xyz)
operator fun get(column: Int) = when(column) {
0 -> x
1 -> y
2 -> z
3 -> w
else -> throw IllegalArgumentException("column must be in 0..3")
}
operator fun get(column: Int, row: Int) = get(column)[row]
operator fun get(column: MatrixColumn) = when(column) {
MatrixColumn.X -> x
MatrixColumn.Y -> y
MatrixColumn.Z -> z
MatrixColumn.W -> w
}
operator fun get(column: MatrixColumn, row: Int) = get(column)[row]
operator fun invoke(row: Int, column: Int) = get(column - 1)[row - 1]
operator fun invoke(row: Int, column: Int, v: Float) = set(column - 1, row - 1, v)
operator fun set(column: Int, v: Float4) {
this[column].xyzw = v
}
operator fun set(column: Int, row: Int, v: Float) {
this[column][row] = v
}
operator fun unaryMinus() = Mat4(-x, -y, -z, -w)
operator fun inc(): Mat4 {
x++
y++
z++
w++
return this
}
operator fun dec(): Mat4 {
x--
y--
z--
w--
return this
}
operator fun plus(v: Float) = Mat4(x + v, y + v, z + v, w + v)
operator fun minus(v: Float) = Mat4(x - v, y - v, z - v, w - v)
operator fun times(v: Float) = Mat4(x * v, y * v, z * v, w * v)
operator fun div(v: Float) = Mat4(x / v, y / v, z / v, w / v)
operator fun times(m: Mat4): Mat4 {
val t = transpose(this)
return Mat4(
Float4(dot(t.x, m.x), dot(t.y, m.x), dot(t.z, m.x), dot(t.w, m.x)),
Float4(dot(t.x, m.y), dot(t.y, m.y), dot(t.z, m.y), dot(t.w, m.y)),
Float4(dot(t.x, m.z), dot(t.y, m.z), dot(t.z, m.z), dot(t.w, m.z)),
Float4(dot(t.x, m.w), dot(t.y, m.w), dot(t.z, m.w), dot(t.w, m.w))
)
}
operator fun times(v: Float4): Float4 {
val t = transpose(this)
return Float4(dot(t.x, v), dot(t.y, v), dot(t.z, v), dot(t.w, v))
}
fun toFloatArray() = floatArrayOf(
x.x, y.x, z.x, w.x,
x.y, y.y, z.y, w.y,
x.z, y.z, z.z, w.z,
x.w, y.w, z.w, w.w
)
override fun toString(): String {
return """
|${x.x} ${y.x} ${z.x} ${w.x}|
|${x.y} ${y.y} ${z.y} ${w.y}|
|${x.z} ${y.z} ${z.z} ${w.z}|
|${x.w} ${y.w} ${z.w} ${w.w}|
""".trimIndent()
}
}
fun transpose(m: Mat2) = Mat2(
Float2(m.x.x, m.y.x),
Float2(m.x.y, m.y.y)
)
fun transpose(m: Mat3) = Mat3(
Float3(m.x.x, m.y.x, m.z.x),
Float3(m.x.y, m.y.y, m.z.y),
Float3(m.x.z, m.y.z, m.z.z)
)
fun inverse(m: Mat3): Mat3 {
val a = m.x.x
val b = m.x.y
val c = m.x.z
val d = m.y.x
val e = m.y.y
val f = m.y.z
val g = m.z.x
val h = m.z.y
val i = m.z.z
val A = e * i - f * h
val B = f * g - d * i
val C = d * h - e * g
val det = a * A + b * B + c * C
return Mat3.of(
A / det, B / det, C / det,
(c * h - b * i) / det, (a * i - c * g) / det, (b * g - a * h) / det,
(b * f - c * e) / det, (c * d - a * f) / det, (a * e - b * d) / det
)
}
fun transpose(m: Mat4) = Mat4(
Float4(m.x.x, m.y.x, m.z.x, m.w.x),
Float4(m.x.y, m.y.y, m.z.y, m.w.y),
Float4(m.x.z, m.y.z, m.z.z, m.w.z),
Float4(m.x.w, m.y.w, m.z.w, m.w.w)
)
fun inverse(m: Mat4): Mat4 {
val result = Mat4()
var pair0 = m.z.z * m.w.w
var pair1 = m.w.z * m.z.w
var pair2 = m.y.z * m.w.w
var pair3 = m.w.z * m.y.w
var pair4 = m.y.z * m.z.w
var pair5 = m.z.z * m.y.w
var pair6 = m.x.z * m.w.w
var pair7 = m.w.z * m.x.w
var pair8 = m.x.z * m.z.w
var pair9 = m.z.z * m.x.w
var pair10 = m.x.z * m.y.w
var pair11 = m.y.z * m.x.w
result.x.x = pair0 * m.y.y + pair3 * m.z.y + pair4 * m.w.y
result.x.x -= pair1 * m.y.y + pair2 * m.z.y + pair5 * m.w.y
result.x.y = pair1 * m.x.y + pair6 * m.z.y + pair9 * m.w.y
result.x.y -= pair0 * m.x.y + pair7 * m.z.y + pair8 * m.w.y
result.x.z = pair2 * m.x.y + pair7 * m.y.y + pair10 * m.w.y
result.x.z -= pair3 * m.x.y + pair6 * m.y.y + pair11 * m.w.y
result.x.w = pair5 * m.x.y + pair8 * m.y.y + pair11 * m.z.y
result.x.w -= pair4 * m.x.y + pair9 * m.y.y + pair10 * m.z.y
result.y.x = pair1 * m.y.x + pair2 * m.z.x + pair5 * m.w.x
result.y.x -= pair0 * m.y.x + pair3 * m.z.x + pair4 * m.w.x
result.y.y = pair0 * m.x.x + pair7 * m.z.x + pair8 * m.w.x
result.y.y -= pair1 * m.x.x + pair6 * m.z.x + pair9 * m.w.x
result.y.z = pair3 * m.x.x + pair6 * m.y.x + pair11 * m.w.x
result.y.z -= pair2 * m.x.x + pair7 * m.y.x + pair10 * m.w.x
result.y.w = pair4 * m.x.x + pair9 * m.y.x + pair10 * m.z.x
result.y.w -= pair5 * m.x.x + pair8 * m.y.x + pair11 * m.z.x
pair0 = m.z.x * m.w.y
pair1 = m.w.x * m.z.y
pair2 = m.y.x * m.w.y
pair3 = m.w.x * m.y.y
pair4 = m.y.x * m.z.y
pair5 = m.z.x * m.y.y
pair6 = m.x.x * m.w.y
pair7 = m.w.x * m.x.y
pair8 = m.x.x * m.z.y
pair9 = m.z.x * m.x.y
pair10 = m.x.x * m.y.y
pair11 = m.y.x * m.x.y
result.z.x = pair0 * m.y.w + pair3 * m.z.w + pair4 * m.w.w
result.z.x -= pair1 * m.y.w + pair2 * m.z.w + pair5 * m.w.w
result.z.y = pair1 * m.x.w + pair6 * m.z.w + pair9 * m.w.w
result.z.y -= pair0 * m.x.w + pair7 * m.z.w + pair8 * m.w.w
result.z.z = pair2 * m.x.w + pair7 * m.y.w + pair10 * m.w.w
result.z.z -= pair3 * m.x.w + pair6 * m.y.w + pair11 * m.w.w
result.z.w = pair5 * m.x.w + pair8 * m.y.w + pair11 * m.z.w
result.z.w -= pair4 * m.x.w + pair9 * m.y.w + pair10 * m.z.w
result.w.x = pair2 * m.z.z + pair5 * m.w.z + pair1 * m.y.z
result.w.x -= pair4 * m.w.z + pair0 * m.y.z + pair3 * m.z.z
result.w.y = pair8 * m.w.z + pair0 * m.x.z + pair7 * m.z.z
result.w.y -= pair6 * m.z.z + pair9 * m.w.z + pair1 * m.x.z
result.w.z = pair6 * m.y.z + pair11 * m.w.z + pair3 * m.x.z
result.w.z -= pair10 * m.w.z + pair2 * m.x.z + pair7 * m.y.z
result.w.w = pair10 * m.z.z + pair4 * m.x.z + pair9 * m.y.z
result.w.w -= pair8 * m.y.z + pair11 * m.z.z + pair5 * m.x.z
val determinant = m.x.x * result.x.x + m.y.x * result.x.y + m.z.x * result.x.z + m.w.x * result.x.w
return result / determinant
}
fun scale(s: Float3) = Mat4(Float4(x = s.x), Float4(y = s.y), Float4(z = s.z))
fun scale(m: Mat4) = scale(m.scale)
fun translation(t: Float3) = Mat4(w = Float4(t, 1.0f))
fun translation(m: Mat4) = translation(m.translation)
fun rotation(m: Mat4) = Mat4(normalize(m.right), normalize(m.up), normalize(m.forward))
fun rotation(d: Float3): Mat4 {
val r = transform(d, ::radians)
val c = transform(r, { x -> cos(x) })
val s = transform(r, { x -> sin(x) })
return Mat4.of(
c.y * c.z, -c.x * s.z + s.x * s.y * c.z, s.x * s.z + c.x * s.y * c.z, 0.0f,
c.y * s.z, c.x * c.z + s.x * s.y * s.z, -s.x * c.z + c.x * s.y * s.z, 0.0f,
-s.y , s.x * c.y , c.x * c.y , 0.0f,
0.0f , 0.0f , 0.0f , 1.0f
)
}
fun rotation(axis: Float3, angle: Float): Mat4 {
val x = axis.x
val y = axis.y
val z = axis.z
val r = radians(angle)
val c = cos(r)
val s = sin(r)
val d = 1.0f - c
return Mat4.of(
x * x * d + c , x * y * d - z * s, x * z * d + y * s, 0.0f,
y * x * d + z * s, y * y * d + c , y * z * d - x * s, 0.0f,
z * x * d - y * s, z * y * d + x * s, z * z * d + c , 0.0f,
0.0f , 0.0f , 0.0f , 1.0f
)
}
fun normal(m: Mat4) = scale(1.0f / Float3(length2(m.right), length2(m.up), length2(m.forward))) * m
fun lookAt(eye: Float3, target: Float3, up: Float3 = Float3(z = 1.0f)): Mat4 {
return lookTowards(eye, target - eye, up)
}
fun lookTowards(eye: Float3, forward: Float3, up: Float3 = Float3(z = 1.0f)): Mat4 {
val f = normalize(forward)
val r = normalize(f x up)
val u = normalize(r x f)
return Mat4(Float4(r), Float4(u), Float4(f), Float4(eye, 1.0f))
}
fun perspective(fov: Float, ratio: Float, near: Float, far: Float): Mat4 {
val t = 1.0f / tan(radians(fov) * 0.5f)
val a = (far + near) / (far - near)
val b = (2.0f * far * near) / (far - near)
val c = t / ratio
return Mat4(Float4(x = c), Float4(y = t), Float4(z = a, w = 1.0f), Float4(z = -b))
}
fun ortho(l: Float, r: Float, b: Float, t: Float, n: Float, f: Float) = Mat4(
Float4(x = 2.0f / (r - 1.0f)),
Float4(y = 2.0f / (t - b)),
Float4(z = -2.0f / (f - n)),
Float4(-(r + l) / (r - l), -(t + b) / (t - b), -(f + n) / (f - n), 1.0f)
)

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2017 Romain Guy
*
* 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.utils
data class Ray(var origin: Float3 = Float3(), var direction: Float3)
fun pointAt(r: Ray, t: Float) = r.origin + r.direction * t

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2017 Romain Guy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE")
package com.google.android.filament.utils
const val FPI = 3.1415926536f
const val HALF_PI = FPI * 0.5f
const val TWO_PI = FPI * 2.0f
const val FOUR_PI = FPI * 4.0f
const val INV_PI = 1.0f / FPI
const val INV_TWO_PI = INV_PI * 0.5f
const val INV_FOUR_PI = INV_PI * 0.25f
inline fun clamp(x: Float, min: Float, max: Float)= if (x < min) min else (if (x > max) max else x)
inline fun saturate(x: Float) = clamp(x, 0.0f, 1.0f)
inline fun mix(a: Float, b: Float, x: Float) = a * (1.0f - x) + b * x
inline fun degrees(v: Float) = v * (180.0f * INV_PI)
inline fun radians(v: Float) = v * (FPI / 180.0f)
inline fun fract(v: Float) = v % 1
inline fun sqr(v: Float) = v * v
inline fun pow(x: Float, y: Float) = StrictMath.pow(x.toDouble(), y.toDouble()).toFloat()

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 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.utils;
public class Utils {
/**
* Initializes the utils JNI layer. Must be called before using any utils functionality.
*/
public static void init() {
System.loadLibrary("filament-utils-jni");
}
}

View File

@@ -21,10 +21,6 @@ add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
add_library(image STATIC IMPORTED)
set_target_properties(image PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libimage.a)
add_library(ibl STATIC IMPORTED)
set_target_properties(ibl PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl.a)
@@ -61,7 +57,6 @@ add_library(gltfio-jni-obj OBJECT
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
src/main/cpp/FilamentAsset.cpp
src/main/cpp/KtxLoader.cpp
src/main/cpp/MaterialProvider.cpp
src/main/cpp/ResourceLoader.cpp)
@@ -91,7 +86,6 @@ if (NOT DISABLE_GLTFIO_JNI)
filaflat
filabridge
geometry
image
ibl
utils
log

View File

@@ -42,4 +42,5 @@ dependencies {
implementation deps.kotlin
implementation project(':filament-android')
implementation project(':gltfio-android')
implementation project(':filament-utils-android')
}

View File

@@ -27,6 +27,7 @@ import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.android.UiHelper
import com.google.android.filament.gltfio.*
import com.google.android.filament.utils.*
import java.nio.ByteBuffer
import java.nio.ByteOrder
@@ -46,7 +47,7 @@ class MainActivity : Activity() {
companion object {
init {
Gltfio.init()
Utils.init()
}
}

View File

@@ -2,6 +2,7 @@
include ':filament-android'
include ':filamat-android'
include ':gltfio-android'
include ':filament-utils-android'
// Samples
include ':samples:sample-gltf-bloom'

View File

@@ -129,6 +129,8 @@ function build_clean {
rm -Rf android/filamat-android/build android/filamat-android/.cxx
rm -Rf android/gltfio-android/build android/gltfio-android/.externalNativeBuild
rm -Rf android/gltfio-android/build android/gltfio-android/.cxx
rm -Rf android/filament-utils-android/build android/filament-utils-android/.externalNativeBuild
rm -Rf android/filament-utils-android/build android/filament-utils-android/.cxx
}
function build_desktop_target {
@@ -373,7 +375,8 @@ function build_android {
-Pfilament_dist_dir=../out/android-debug/filament \
-Pextra_cmake_args=${VULKAN_ANDROID_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug
:gltfio-android:assembleDebug \
:filament-utils-android:assembleDebug
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
@@ -389,6 +392,9 @@ function build_android {
echo "Installing out/gltfio-android-debug.aar..."
cp gltfio-android/build/outputs/aar/gltfio-android-debug.aar ../out/
echo "Installing out/filament-utils-android-debug.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-debug.aar ../out/
fi
fi
@@ -396,7 +402,8 @@ function build_android {
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease
:gltfio-android:assembleRelease \
:filament-utils-android:assembleRelease
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
@@ -412,6 +419,9 @@ function build_android {
echo "Installing out/gltfio-android-release.aar..."
cp gltfio-android/build/outputs/aar/gltfio-android-release.aar ../out/
echo "Installing out/filament-utils-android-debug.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-release.aar ../out/
fi
fi