diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b60c71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Android Studio configuration. +*.iml +.idea/ + +# Gradle configuration. +.gradle/ +build/ + +# User configuration. +local.properties + +# OS configurations. +.DS_Store diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..a2def88 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +/build +google-services.json diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f99749f --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' + +def setVersionName = { -> + if (project.hasProperty("androidVersionName")) { + println("Replacing versionName with supplied build parameter: " + + "$androidVersionName") + return androidVersionName + } else { + return "1.0" + } +} + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.google.ar.core.examples.java.helloar" + + // AR Optional apps must declare minSdkVersion >= 14. + // AR Required apps must declare minSdkVersion >= 24. + minSdkVersion 24 + targetSdkVersion 28 + versionCode 1 + versionName setVersionName() + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + // ARCore library + // Dependency version is rewritten at build time. + implementation 'com.google.ar:core:1.13.0' + + // Obj - a simple Wavefront OBJ file loader + // https://github.com/javagl/Obj + implementation 'de.javagl:obj:0.2.1' + + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:design:28.0.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..45dc58a --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /opt/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2c6a34c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/models/andy.obj b/app/src/main/assets/models/andy.obj new file mode 100644 index 0000000..9097dea --- /dev/null +++ b/app/src/main/assets/models/andy.obj @@ -0,0 +1,5003 @@ +# This file uses centimeters as units for non-parametric coordinates. + +g default +v 0.036531 0.203676 -0.001768 +v 0.035000 0.204560 -0.002500 +v 0.033469 0.205443 -0.001768 +v 0.032835 0.205810 -0.000000 +v 0.033469 0.205443 0.001768 +v 0.035000 0.204560 0.002500 +v 0.036531 0.203676 0.001768 +v 0.037165 0.203310 -0.000000 +v 0.036951 0.204877 -0.001531 +v 0.035625 0.205642 -0.002165 +v 0.034299 0.206408 -0.001531 +v 0.033750 0.206725 -0.000000 +v 0.034299 0.206408 0.001531 +v 0.035625 0.205642 0.002165 +v 0.036951 0.204877 0.001531 +v 0.037500 0.204560 -0.000000 +v 0.036848 0.205993 -0.000884 +v 0.036083 0.206435 -0.001250 +v 0.035317 0.206877 -0.000884 +v 0.035000 0.207060 -0.000000 +v 0.035317 0.206877 0.000884 +v 0.036083 0.206435 0.001250 +v 0.036848 0.205993 0.000884 +v 0.037165 0.205810 -0.000000 +v 0.036250 0.206725 -0.000000 +v 0.027420 0.185957 0.006258 +v 0.020986 0.188283 0.004790 +v 0.020986 0.188283 -0.004790 +v 0.027420 0.185957 -0.006258 +v 0.028125 0.185957 0.000000 +v 0.021526 0.188283 0.000000 +v 0.024935 0.187127 0.002956 +v 0.026910 0.186366 0.002090 +v 0.022982 0.187924 0.002090 +v 0.024935 0.187127 -0.002956 +v 0.022982 0.187924 -0.002090 +v 0.026910 0.186366 -0.002090 +v 0.027766 0.186119 0.000000 +v 0.022169 0.188170 0.000000 +v -0.027420 0.185957 0.006258 +v -0.020986 0.188283 0.004790 +v -0.020986 0.188283 -0.004790 +v -0.027420 0.185957 -0.006258 +v -0.028125 0.185957 0.000000 +v -0.021526 0.188283 0.000000 +v -0.024935 0.187127 0.002956 +v -0.026910 0.186366 0.002090 +v -0.022982 0.187924 0.002090 +v -0.024935 0.187127 -0.002956 +v -0.022982 0.187924 -0.002090 +v -0.026910 0.186366 -0.002090 +v -0.027766 0.186119 0.000000 +v -0.022169 0.188170 0.000000 +v 0.000000 0.050001 -0.056250 +v -0.012517 0.050001 -0.054840 +v -0.024406 0.050001 -0.050679 +v -0.035071 0.050001 -0.043978 +v -0.043978 0.050001 -0.035071 +v -0.050679 0.050001 -0.024406 +v -0.054840 0.050001 -0.012517 +v -0.056250 0.050001 0.000000 +v -0.054840 0.050001 0.012517 +v -0.050679 0.050001 0.024406 +v -0.043978 0.050001 0.035071 +v -0.035071 0.050001 0.043978 +v -0.024406 0.050001 0.050679 +v -0.012517 0.050001 0.054840 +v 0.000000 0.050001 0.056250 +v 0.000000 0.040000 -0.046217 +v -0.010284 0.040000 -0.045058 +v -0.020053 0.040000 -0.041640 +v -0.028816 0.040000 -0.036134 +v -0.036134 0.040000 -0.028816 +v -0.041640 0.040000 -0.020053 +v -0.045058 0.040000 -0.010284 +v -0.046217 0.040000 0.000000 +v -0.045058 0.040000 0.010284 +v -0.041640 0.040000 0.020053 +v -0.036134 0.040000 0.028816 +v -0.028816 0.040000 0.036134 +v -0.020053 0.040000 0.041640 +v -0.010284 0.040000 0.045058 +v 0.000000 0.040000 0.046217 +v -0.034725 0.046910 0.043544 +v -0.024165 0.046910 0.050180 +v -0.012393 0.046910 0.054299 +v 0.000000 0.046910 0.055695 +v 0.000000 0.046910 -0.055695 +v -0.012393 0.046910 -0.054299 +v -0.024165 0.046910 -0.050180 +v -0.034725 0.046910 -0.043544 +v -0.043544 0.046910 -0.034725 +v -0.050180 0.046910 -0.024165 +v -0.054299 0.046910 -0.012393 +v -0.055695 0.046910 0.000000 +v -0.054299 0.046910 0.012393 +v -0.050180 0.046910 0.024165 +v -0.043544 0.046910 0.034725 +v -0.040731 0.041910 0.032482 +v -0.032482 0.041910 0.040731 +v -0.022604 0.041910 0.046938 +v -0.011593 0.041910 0.050791 +v 0.000000 0.041910 0.052098 +v 0.000000 0.041910 -0.052098 +v -0.011593 0.041910 -0.050791 +v -0.022604 0.041910 -0.046938 +v -0.032482 0.041910 -0.040731 +v -0.040731 0.041910 -0.032482 +v -0.046938 0.041910 -0.022604 +v -0.050791 0.041910 -0.011593 +v -0.052098 0.041910 0.000000 +v -0.050791 0.041910 0.011593 +v -0.046938 0.041910 0.022604 +v 0.012517 0.050001 -0.054840 +v 0.024406 0.050001 -0.050679 +v 0.035071 0.050001 -0.043978 +v 0.043978 0.050001 -0.035071 +v 0.050679 0.050001 -0.024406 +v 0.054840 0.050001 -0.012517 +v 0.056250 0.050001 0.000000 +v 0.054840 0.050001 0.012517 +v 0.050679 0.050001 0.024406 +v 0.043978 0.050001 0.035071 +v 0.035071 0.050001 0.043978 +v 0.024406 0.050001 0.050679 +v 0.012517 0.050001 0.054840 +v 0.010284 0.040000 -0.045058 +v 0.020053 0.040000 -0.041640 +v 0.028816 0.040000 -0.036134 +v 0.036134 0.040000 -0.028816 +v 0.041640 0.040000 -0.020053 +v 0.045058 0.040000 -0.010284 +v 0.046217 0.040000 0.000000 +v 0.045058 0.040000 0.010284 +v 0.041640 0.040000 0.020053 +v 0.036134 0.040000 0.028816 +v 0.028816 0.040000 0.036134 +v 0.020053 0.040000 0.041640 +v 0.010284 0.040000 0.045058 +v 0.034725 0.046910 0.043544 +v 0.024165 0.046910 0.050180 +v 0.012393 0.046910 0.054299 +v 0.012393 0.046910 -0.054299 +v 0.024165 0.046910 -0.050180 +v 0.034725 0.046910 -0.043544 +v 0.043544 0.046910 -0.034725 +v 0.050180 0.046910 -0.024165 +v 0.054299 0.046910 -0.012393 +v 0.055695 0.046910 0.000000 +v 0.054299 0.046910 0.012393 +v 0.050180 0.046910 0.024165 +v 0.043544 0.046910 0.034725 +v 0.040731 0.041910 0.032482 +v 0.032482 0.041910 0.040731 +v 0.022604 0.041910 0.046938 +v 0.011593 0.041910 0.050791 +v 0.011593 0.041910 -0.050791 +v 0.022604 0.041910 -0.046938 +v 0.032482 0.041910 -0.040731 +v 0.040731 0.041910 -0.032482 +v 0.046938 0.041910 -0.022604 +v 0.050791 0.041910 -0.011593 +v 0.052098 0.041910 0.000000 +v 0.050791 0.041910 0.011593 +v 0.046938 0.041910 0.022604 +v 0.000000 0.137500 0.000000 +v 0.000000 0.137500 0.054803 +v 0.000000 0.136062 0.056250 +v -0.012517 0.136062 0.054840 +v -0.012195 0.137500 0.053429 +v -0.024406 0.136062 0.050679 +v -0.023778 0.137500 0.049376 +v -0.035071 0.136062 0.043978 +v -0.034169 0.137500 0.042847 +v -0.043978 0.136062 0.035071 +v -0.042847 0.137500 0.034169 +v -0.050679 0.136062 0.024406 +v -0.049376 0.137500 0.023778 +v -0.054840 0.136062 0.012517 +v -0.053429 0.137500 0.012195 +v -0.056250 0.136062 0.000000 +v -0.054803 0.137500 0.000000 +v -0.054840 0.136062 -0.012517 +v -0.053429 0.137500 -0.012195 +v -0.050679 0.136062 -0.024406 +v -0.049376 0.137500 -0.023778 +v -0.043978 0.136062 -0.035071 +v -0.042847 0.137500 -0.034169 +v -0.035071 0.136062 -0.043978 +v -0.034169 0.137500 -0.042847 +v -0.024406 0.136062 -0.050679 +v -0.023778 0.137500 -0.049376 +v -0.012517 0.136062 -0.054840 +v -0.012195 0.137500 -0.053429 +v 0.000000 0.136062 -0.056250 +v 0.000000 0.137500 -0.054803 +v 0.012517 0.136062 0.054840 +v 0.012195 0.137500 0.053429 +v 0.024406 0.136062 0.050679 +v 0.023778 0.137500 0.049376 +v 0.035071 0.136062 0.043978 +v 0.034169 0.137500 0.042847 +v 0.043978 0.136062 0.035071 +v 0.042847 0.137500 0.034169 +v 0.050679 0.136062 0.024406 +v 0.049376 0.137500 0.023778 +v 0.054840 0.136062 0.012517 +v 0.053429 0.137500 0.012195 +v 0.056250 0.136062 0.000000 +v 0.054803 0.137500 0.000000 +v 0.054840 0.136062 -0.012517 +v 0.053429 0.137500 -0.012195 +v 0.050679 0.136062 -0.024406 +v 0.049376 0.137500 -0.023778 +v 0.043978 0.136062 -0.035071 +v 0.042847 0.137500 -0.034169 +v 0.035071 0.136062 -0.043978 +v 0.034169 0.137500 -0.042847 +v 0.024406 0.136062 -0.050679 +v 0.023778 0.137500 -0.049376 +v 0.012517 0.136062 -0.054840 +v 0.012195 0.137500 -0.053429 +v 0.000000 0.153477 -0.055769 +v -0.012410 0.153477 -0.054370 +v -0.024197 0.153477 -0.050246 +v -0.034771 0.153477 -0.043602 +v -0.043602 0.153477 -0.034771 +v -0.050246 0.153477 -0.024197 +v -0.054370 0.153477 -0.012410 +v -0.055769 0.153477 0.000000 +v -0.054370 0.153477 0.012410 +v -0.050246 0.153477 0.024197 +v -0.043602 0.153477 0.034771 +v -0.034771 0.153477 0.043602 +v -0.024197 0.153477 0.050246 +v -0.012410 0.153477 0.054370 +v 0.000000 0.153477 0.055769 +v 0.000000 0.159345 -0.054333 +v -0.012090 0.159345 -0.052971 +v -0.023574 0.159345 -0.048953 +v -0.033876 0.159345 -0.042479 +v -0.042480 0.159345 -0.033876 +v -0.048953 0.159345 -0.023574 +v -0.052971 0.159345 -0.012090 +v -0.054333 0.159345 0.000000 +v -0.052971 0.159345 0.012090 +v -0.048953 0.159345 0.023574 +v -0.042480 0.159345 0.033876 +v -0.033876 0.159345 0.042480 +v -0.023574 0.159345 0.048953 +v -0.012090 0.159345 0.052971 +v 0.000000 0.159345 0.054333 +v 0.000000 0.164974 -0.051968 +v -0.011564 0.164974 -0.050665 +v -0.022548 0.164974 -0.046822 +v -0.032402 0.164974 -0.040630 +v -0.040630 0.164974 -0.032402 +v -0.046822 0.164974 -0.022548 +v -0.050665 0.164974 -0.011564 +v -0.051968 0.164974 0.000000 +v -0.050665 0.164974 0.011564 +v -0.046822 0.164974 0.022548 +v -0.040630 0.164974 0.032402 +v -0.032402 0.164974 0.040630 +v -0.022548 0.164974 0.046822 +v -0.011564 0.164974 0.050665 +v 0.000000 0.164974 0.051968 +v 0.000000 0.170245 -0.048714 +v -0.010840 0.170245 -0.047493 +v -0.021136 0.170245 -0.043890 +v -0.030373 0.170245 -0.038086 +v -0.038086 0.170245 -0.030373 +v -0.043890 0.170245 -0.021136 +v -0.047493 0.170245 -0.010840 +v -0.048714 0.170245 0.000000 +v -0.047493 0.170245 0.010840 +v -0.043890 0.170245 0.021136 +v -0.038086 0.170245 0.030373 +v -0.030373 0.170245 0.038086 +v -0.021136 0.170245 0.043890 +v -0.010840 0.170245 0.047493 +v 0.000000 0.170245 0.048714 +v 0.000000 0.175052 -0.044626 +v -0.009930 0.175052 -0.043507 +v -0.019363 0.175052 -0.040207 +v -0.027824 0.175052 -0.034890 +v -0.034890 0.175052 -0.027824 +v -0.040207 0.175052 -0.019363 +v -0.043507 0.175052 -0.009930 +v -0.044626 0.175052 0.000000 +v -0.043507 0.175052 0.009930 +v -0.040207 0.175052 0.019363 +v -0.034890 0.175052 0.027824 +v -0.027824 0.175052 0.034890 +v -0.019363 0.175052 0.040207 +v -0.009930 0.175052 0.043507 +v 0.000000 0.175052 0.044626 +v 0.000000 0.179312 -0.039775 +v -0.008851 0.179312 -0.038777 +v -0.017258 0.179312 -0.035836 +v -0.024799 0.179312 -0.031097 +v -0.031097 0.179312 -0.024799 +v -0.035836 0.179312 -0.017258 +v -0.038777 0.179312 -0.008851 +v -0.039775 0.179312 0.000000 +v -0.038777 0.179312 0.008851 +v -0.035836 0.179312 0.017258 +v -0.031097 0.179312 0.024799 +v -0.024799 0.179312 0.031097 +v -0.017258 0.179312 0.035836 +v -0.008851 0.179312 0.038778 +v 0.000000 0.179312 0.039775 +v 0.000000 0.182961 -0.034243 +v -0.007620 0.182961 -0.033384 +v -0.014857 0.182961 -0.030852 +v -0.021350 0.182961 -0.026772 +v -0.026772 0.182961 -0.021350 +v -0.030852 0.182961 -0.014857 +v -0.033384 0.182961 -0.007620 +v -0.034243 0.182961 0.000000 +v -0.033384 0.182961 0.007620 +v -0.030852 0.182961 0.014857 +v -0.026772 0.182961 0.021350 +v -0.021350 0.182961 0.026772 +v -0.014857 0.182961 0.030852 +v -0.007620 0.182961 0.033384 +v 0.000000 0.182961 0.034243 +v 0.000000 0.185957 -0.028125 +v -0.006258 0.185957 -0.027420 +v -0.012203 0.185957 -0.025340 +v -0.017536 0.185957 -0.021989 +v -0.021989 0.185957 -0.017536 +v -0.025340 0.185957 -0.012203 +v -0.025340 0.185957 0.012203 +v -0.021989 0.185957 0.017536 +v -0.017536 0.185957 0.021989 +v -0.012203 0.185957 0.025340 +v -0.006258 0.185957 0.027420 +v 0.000000 0.185957 0.028125 +v 0.000000 0.188283 -0.021526 +v -0.004790 0.188283 -0.020986 +v -0.009340 0.188283 -0.019394 +v -0.013421 0.188283 -0.016830 +v -0.016830 0.188283 -0.013421 +v -0.019394 0.188283 -0.009340 +v -0.019394 0.188283 0.009340 +v -0.016830 0.188283 0.013421 +v -0.013421 0.188283 0.016830 +v -0.009340 0.188283 0.019394 +v -0.004790 0.188283 0.020986 +v 0.000000 0.188283 0.021526 +v 0.000000 0.189936 -0.014559 +v -0.003240 0.189936 -0.014194 +v -0.006317 0.189936 -0.013117 +v -0.009077 0.189936 -0.011382 +v -0.011382 0.189936 -0.009077 +v -0.013117 0.189936 -0.006317 +v -0.014194 0.189936 -0.003240 +v -0.014559 0.189936 0.000000 +v -0.014194 0.189936 0.003240 +v -0.013117 0.189936 0.006317 +v -0.011382 0.189936 0.009077 +v -0.009077 0.189936 0.011382 +v -0.006317 0.189936 0.013117 +v -0.003240 0.189936 0.014194 +v 0.000000 0.189936 0.014559 +v 0.000000 0.190922 -0.007342 +v -0.001634 0.190922 -0.007158 +v -0.003186 0.190922 -0.006615 +v -0.004578 0.190922 -0.005740 +v -0.005740 0.190922 -0.004578 +v -0.006615 0.190922 -0.003186 +v -0.007158 0.190922 -0.001634 +v -0.007342 0.190922 0.000000 +v -0.007158 0.190922 0.001634 +v -0.006615 0.190922 0.003186 +v -0.005740 0.190922 0.004578 +v -0.004578 0.190922 0.005740 +v -0.003186 0.190922 0.006615 +v -0.001634 0.190922 0.007158 +v 0.000000 0.190922 0.007342 +v 0.000000 0.191250 0.000000 +v 0.012410 0.153477 -0.054370 +v 0.024197 0.153477 -0.050246 +v 0.034771 0.153477 -0.043602 +v 0.043602 0.153477 -0.034771 +v 0.050246 0.153477 -0.024197 +v 0.054370 0.153477 -0.012410 +v 0.055769 0.153477 0.000000 +v 0.054370 0.153477 0.012410 +v 0.050246 0.153477 0.024197 +v 0.043602 0.153477 0.034771 +v 0.034771 0.153477 0.043602 +v 0.024197 0.153477 0.050246 +v 0.012410 0.153477 0.054370 +v 0.012090 0.159345 -0.052971 +v 0.023574 0.159345 -0.048953 +v 0.033876 0.159345 -0.042479 +v 0.042479 0.159345 -0.033876 +v 0.048953 0.159345 -0.023574 +v 0.052971 0.159345 -0.012090 +v 0.054333 0.159345 0.000000 +v 0.052971 0.159345 0.012090 +v 0.048953 0.159345 0.023574 +v 0.042480 0.159345 0.033876 +v 0.033876 0.159345 0.042480 +v 0.023574 0.159345 0.048953 +v 0.012090 0.159345 0.052971 +v 0.011564 0.164974 -0.050665 +v 0.022548 0.164974 -0.046822 +v 0.032402 0.164974 -0.040630 +v 0.040630 0.164974 -0.032402 +v 0.046822 0.164974 -0.022548 +v 0.050665 0.164974 -0.011564 +v 0.051968 0.164974 0.000000 +v 0.050665 0.164974 0.011564 +v 0.046822 0.164974 0.022548 +v 0.040630 0.164974 0.032402 +v 0.032402 0.164974 0.040630 +v 0.022548 0.164974 0.046822 +v 0.011564 0.164974 0.050665 +v 0.010840 0.170245 -0.047493 +v 0.021136 0.170245 -0.043890 +v 0.030373 0.170245 -0.038086 +v 0.038086 0.170245 -0.030373 +v 0.043890 0.170245 -0.021136 +v 0.047493 0.170245 -0.010840 +v 0.048714 0.170245 0.000000 +v 0.047493 0.170245 0.010840 +v 0.043890 0.170245 0.021136 +v 0.038086 0.170245 0.030373 +v 0.030373 0.170245 0.038086 +v 0.021136 0.170245 0.043890 +v 0.010840 0.170245 0.047493 +v 0.009930 0.175052 -0.043507 +v 0.019363 0.175052 -0.040207 +v 0.027824 0.175052 -0.034890 +v 0.034890 0.175052 -0.027824 +v 0.040207 0.175052 -0.019363 +v 0.043507 0.175052 -0.009930 +v 0.044626 0.175052 0.000000 +v 0.043507 0.175052 0.009930 +v 0.040207 0.175052 0.019363 +v 0.034890 0.175052 0.027824 +v 0.027824 0.175052 0.034890 +v 0.019363 0.175052 0.040207 +v 0.009930 0.175052 0.043507 +v 0.008851 0.179312 -0.038777 +v 0.017258 0.179312 -0.035836 +v 0.024799 0.179312 -0.031097 +v 0.031097 0.179312 -0.024799 +v 0.035836 0.179312 -0.017258 +v 0.038777 0.179312 -0.008851 +v 0.039775 0.179312 0.000000 +v 0.038777 0.179312 0.008851 +v 0.035836 0.179312 0.017258 +v 0.031097 0.179312 0.024799 +v 0.024799 0.179312 0.031097 +v 0.017258 0.179312 0.035836 +v 0.008851 0.179312 0.038777 +v 0.007620 0.182961 -0.033384 +v 0.014857 0.182961 -0.030852 +v 0.021350 0.182961 -0.026772 +v 0.026772 0.182961 -0.021350 +v 0.030852 0.182961 -0.014857 +v 0.033384 0.182961 -0.007620 +v 0.034243 0.182961 0.000000 +v 0.033384 0.182961 0.007620 +v 0.030852 0.182961 0.014857 +v 0.026772 0.182961 0.021350 +v 0.021350 0.182961 0.026772 +v 0.014857 0.182961 0.030852 +v 0.007620 0.182961 0.033384 +v 0.006258 0.185957 -0.027420 +v 0.012203 0.185957 -0.025340 +v 0.017536 0.185957 -0.021989 +v 0.021989 0.185957 -0.017536 +v 0.025340 0.185957 -0.012203 +v 0.025340 0.185957 0.012203 +v 0.021989 0.185957 0.017536 +v 0.017536 0.185957 0.021989 +v 0.012203 0.185957 0.025340 +v 0.006258 0.185957 0.027420 +v 0.004790 0.188283 -0.020986 +v 0.009340 0.188283 -0.019394 +v 0.013421 0.188283 -0.016830 +v 0.016830 0.188283 -0.013421 +v 0.019394 0.188283 -0.009340 +v 0.019394 0.188283 0.009340 +v 0.016830 0.188283 0.013421 +v 0.013421 0.188283 0.016830 +v 0.009340 0.188283 0.019394 +v 0.004790 0.188283 0.020986 +v 0.003240 0.189936 -0.014194 +v 0.006317 0.189936 -0.013117 +v 0.009077 0.189936 -0.011382 +v 0.011382 0.189936 -0.009077 +v 0.013117 0.189936 -0.006317 +v 0.014194 0.189936 -0.003240 +v 0.014559 0.189936 0.000000 +v 0.014194 0.189936 0.003240 +v 0.013117 0.189936 0.006317 +v 0.011382 0.189936 0.009077 +v 0.009077 0.189936 0.011382 +v 0.006317 0.189936 0.013117 +v 0.003240 0.189936 0.014194 +v 0.001634 0.190922 -0.007158 +v 0.003186 0.190922 -0.006615 +v 0.004578 0.190922 -0.005740 +v 0.005740 0.190922 -0.004578 +v 0.006615 0.190922 -0.003186 +v 0.007158 0.190922 -0.001634 +v 0.007342 0.190922 0.000000 +v 0.007158 0.190922 0.001634 +v 0.006615 0.190922 0.003186 +v 0.005740 0.190922 0.004578 +v 0.004578 0.190922 0.005740 +v 0.003186 0.190922 0.006615 +v 0.001634 0.190922 0.007158 +v 0.000000 0.147500 -0.054742 +v 0.000000 0.148994 -0.056130 +v -0.012490 0.148994 -0.054723 +v -0.012181 0.147500 -0.053369 +v -0.024354 0.148994 -0.050571 +v -0.023751 0.147500 -0.049320 +v -0.034996 0.148994 -0.043884 +v -0.034131 0.147500 -0.042799 +v -0.043884 0.148994 -0.034996 +v -0.042799 0.147500 -0.034131 +v -0.050571 0.148994 -0.024354 +v -0.049320 0.147500 -0.023751 +v -0.054723 0.148994 -0.012490 +v -0.053369 0.147500 -0.012181 +v -0.056130 0.148994 0.000000 +v -0.054742 0.147500 0.000000 +v -0.054723 0.148994 0.012490 +v -0.053369 0.147500 0.012181 +v -0.050571 0.148994 0.024354 +v -0.049320 0.147500 0.023751 +v -0.043884 0.148994 0.034996 +v -0.042799 0.147500 0.034131 +v -0.034996 0.148994 0.043884 +v -0.034131 0.147500 0.042799 +v -0.024354 0.148994 0.050571 +v -0.023751 0.147500 0.049320 +v -0.012490 0.148994 0.054723 +v -0.012181 0.147500 0.053369 +v 0.000000 0.148994 0.056130 +v 0.000000 0.147500 0.054742 +v 0.012490 0.148994 -0.054723 +v 0.012181 0.147500 -0.053369 +v 0.024354 0.148994 -0.050571 +v 0.023751 0.147500 -0.049320 +v 0.034996 0.148994 -0.043884 +v 0.034131 0.147500 -0.042799 +v 0.043884 0.148994 -0.034996 +v 0.042799 0.147500 -0.034131 +v 0.050571 0.148994 -0.024354 +v 0.049320 0.147500 -0.023751 +v 0.054723 0.148994 -0.012490 +v 0.053369 0.147500 -0.012181 +v 0.056130 0.148994 0.000000 +v 0.054742 0.147500 0.000000 +v 0.054723 0.148994 0.012490 +v 0.053369 0.147500 0.012181 +v 0.050571 0.148994 0.024354 +v 0.049320 0.147500 0.023751 +v 0.043884 0.148994 0.034996 +v 0.042799 0.147500 0.034131 +v 0.034996 0.148994 0.043884 +v 0.034131 0.147500 0.042799 +v 0.024354 0.148994 0.050571 +v 0.023751 0.147500 0.049320 +v 0.012490 0.148994 0.054723 +v 0.012181 0.147500 0.053369 +v 0.000000 0.147500 0.000000 +v -0.054402 0.044123 0.000000 +v -0.053038 0.044123 0.012106 +v -0.049014 0.044123 0.023604 +v -0.042533 0.044123 0.033919 +v -0.033919 0.044123 0.042533 +v -0.023604 0.044123 0.049014 +v -0.012106 0.044123 0.053038 +v 0.000000 0.044123 0.054402 +v 0.012106 0.044123 0.053038 +v 0.023604 0.044123 0.049014 +v 0.033919 0.044123 0.042533 +v 0.042533 0.044123 0.033919 +v 0.049014 0.044123 0.023604 +v 0.053038 0.044123 0.012106 +v 0.054402 0.044123 0.000000 +v 0.053038 0.044123 -0.012106 +v 0.049014 0.044123 -0.023604 +v 0.042533 0.044123 -0.033919 +v 0.033919 0.044123 -0.042533 +v 0.023604 0.044123 -0.049014 +v 0.012105 0.044123 -0.053038 +v 0.000000 0.044123 -0.054402 +v -0.012105 0.044123 -0.053038 +v -0.023604 0.044123 -0.049014 +v -0.033919 0.044123 -0.042533 +v -0.042533 0.044123 -0.033919 +v -0.049014 0.044123 -0.023604 +v -0.053038 0.044123 -0.012106 +v -0.049345 0.040490 0.000000 +v -0.048108 0.040490 0.010980 +v -0.044458 0.040490 0.021410 +v -0.038579 0.040490 0.030766 +v -0.030766 0.040490 0.038579 +v -0.021410 0.040490 0.044458 +v -0.010980 0.040490 0.048108 +v 0.000000 0.040490 0.049345 +v 0.010980 0.040490 0.048108 +v 0.021410 0.040490 0.044458 +v 0.030766 0.040490 0.038579 +v 0.038579 0.040490 0.030766 +v 0.044458 0.040490 0.021410 +v 0.048108 0.040490 0.010980 +v 0.049345 0.040490 0.000000 +v 0.048108 0.040490 -0.010980 +v 0.044458 0.040490 -0.021410 +v 0.038579 0.040490 -0.030766 +v 0.030766 0.040490 -0.038579 +v 0.021410 0.040490 -0.044458 +v 0.010980 0.040490 -0.048108 +v 0.000000 0.040490 -0.049345 +v -0.010980 0.040490 -0.048108 +v -0.021410 0.040490 -0.044458 +v -0.030766 0.040490 -0.038579 +v -0.038579 0.040490 -0.030766 +v -0.044458 0.040490 -0.021410 +v -0.048108 0.040490 -0.010980 +v -0.032286 0.040000 -0.000000 +v -0.030472 0.040000 0.006768 +v -0.025518 0.040000 0.011722 +v -0.018750 0.040000 0.013536 +v -0.011982 0.040000 0.011722 +v -0.007028 0.040000 0.006768 +v -0.005214 0.040000 -0.000000 +v -0.007028 0.040000 -0.006768 +v -0.011982 0.040000 -0.011723 +v -0.018750 0.040000 -0.013536 +v -0.025518 0.040000 -0.011723 +v -0.030472 0.040000 -0.006768 +v 0.000000 0.040000 0.000000 +v 0.032286 0.040000 -0.000000 +v 0.030472 0.040000 0.006768 +v 0.025518 0.040000 0.011722 +v 0.018750 0.040000 0.013536 +v 0.011982 0.040000 0.011722 +v 0.007028 0.040000 0.006768 +v 0.005214 0.040000 -0.000000 +v 0.007028 0.040000 -0.006768 +v 0.011982 0.040000 -0.011723 +v 0.018750 0.040000 -0.013536 +v 0.025518 0.040000 -0.011723 +v 0.030472 0.040000 -0.006768 +v 0.031250 0.012500 -0.000000 +v 0.029575 0.012500 0.006250 +v 0.025000 0.012500 0.010825 +v 0.018750 0.012500 0.012500 +v 0.012500 0.012500 0.010825 +v 0.007925 0.012500 0.006250 +v 0.006250 0.012500 -0.000000 +v 0.007925 0.012500 -0.006250 +v 0.012500 0.012500 -0.010826 +v 0.018750 0.012500 -0.012500 +v 0.025000 0.012500 -0.010826 +v 0.029575 0.012500 -0.006250 +v 0.062925 0.125000 -0.006250 +v 0.067500 0.125000 -0.010825 +v 0.073750 0.125000 -0.012500 +v 0.080000 0.125000 -0.010825 +v 0.084575 0.125000 -0.006250 +v 0.086250 0.125000 -0.000000 +v 0.084575 0.125000 0.006250 +v 0.080000 0.125000 0.010825 +v 0.073750 0.125000 0.012500 +v 0.067500 0.125000 0.010825 +v 0.062925 0.125000 0.006250 +v 0.061250 0.125000 -0.000000 +v 0.062925 0.075000 -0.006250 +v 0.067500 0.075000 -0.010825 +v 0.073750 0.075000 -0.012500 +v 0.080000 0.075000 -0.010825 +v 0.084575 0.075000 -0.006250 +v 0.086250 0.075000 0.000000 +v 0.084575 0.075000 0.006250 +v 0.080000 0.075000 0.010825 +v 0.073750 0.075000 0.012500 +v 0.067500 0.075000 0.010825 +v 0.062925 0.075000 0.006250 +v 0.061250 0.075000 0.000000 +v 0.025097 0.187407 0.002500 +v 0.023447 0.188085 0.001768 +v 0.022760 0.188360 0.000000 +v 0.023447 0.188085 -0.001768 +v 0.025097 0.187407 -0.002500 +v 0.026765 0.186760 -0.001768 +v 0.027488 0.186548 0.000000 +v 0.026765 0.186760 0.001768 +v 0.025000 0.039141 0.010825 +v 0.018750 0.039141 0.012500 +v 0.012500 0.039141 0.010825 +v 0.007925 0.039141 0.006250 +v 0.006250 0.039141 -0.000000 +v 0.007925 0.039141 -0.006250 +v 0.012500 0.039141 -0.010826 +v 0.018750 0.039141 -0.012500 +v 0.025000 0.039141 -0.010826 +v 0.029575 0.039141 -0.006250 +v 0.031250 0.039141 -0.000000 +v 0.029575 0.039141 0.006250 +v -0.033469 0.205443 -0.001768 +v -0.035000 0.204560 -0.002500 +v -0.036531 0.203676 -0.001768 +v -0.037165 0.203310 -0.000000 +v -0.036531 0.203676 0.001768 +v -0.035000 0.204560 0.002500 +v -0.033469 0.205443 0.001768 +v -0.032835 0.205810 -0.000000 +v -0.031250 0.012500 -0.000000 +v -0.029575 0.012500 0.006250 +v -0.025000 0.012500 0.010825 +v -0.018750 0.012500 0.012500 +v -0.012500 0.012500 0.010825 +v -0.007925 0.012500 0.006250 +v -0.006250 0.012500 -0.000000 +v -0.007925 0.012500 -0.006250 +v -0.012500 0.012500 -0.010826 +v -0.018750 0.012500 -0.012500 +v -0.025000 0.012500 -0.010826 +v -0.029575 0.012500 -0.006250 +v -0.025097 0.187407 0.002500 +v -0.023447 0.188085 0.001768 +v -0.022760 0.188360 0.000000 +v -0.023447 0.188085 -0.001768 +v -0.025097 0.187407 -0.002500 +v -0.026765 0.186760 -0.001768 +v -0.027488 0.186548 0.000000 +v -0.026765 0.186760 0.001768 +v -0.025000 0.039141 0.010825 +v -0.018750 0.039141 0.012500 +v -0.012500 0.039141 0.010825 +v -0.007925 0.039141 0.006250 +v -0.006250 0.039141 -0.000000 +v -0.007925 0.039141 -0.006250 +v -0.012500 0.039141 -0.010826 +v -0.018750 0.039141 -0.012500 +v -0.025000 0.039141 -0.010826 +v -0.029575 0.039141 -0.006250 +v -0.031250 0.039141 -0.000000 +v -0.029575 0.039141 0.006250 +v -0.036951 0.204877 -0.001531 +v -0.035625 0.205642 -0.002165 +v -0.034299 0.206408 -0.001531 +v -0.033750 0.206725 -0.000000 +v -0.034299 0.206408 0.001531 +v -0.035625 0.205642 0.002165 +v -0.036951 0.204877 0.001531 +v -0.037500 0.204560 -0.000000 +v -0.036848 0.205993 -0.000884 +v -0.036083 0.206435 -0.001250 +v -0.035317 0.206877 -0.000884 +v -0.035000 0.207060 -0.000000 +v -0.035317 0.206877 0.000884 +v -0.036083 0.206435 0.001250 +v -0.036848 0.205993 0.000884 +v -0.037165 0.205810 -0.000000 +v -0.036250 0.206725 -0.000000 +v -0.062925 0.125000 -0.006250 +v -0.067500 0.125000 -0.010825 +v -0.073750 0.125000 -0.012500 +v -0.080000 0.125000 -0.010825 +v -0.084575 0.125000 -0.006250 +v -0.086250 0.125000 -0.000000 +v -0.084575 0.125000 0.006250 +v -0.080000 0.125000 0.010825 +v -0.073750 0.125000 0.012500 +v -0.067500 0.125000 0.010825 +v -0.062925 0.125000 0.006250 +v -0.061250 0.125000 -0.000000 +v -0.062925 0.075000 -0.006250 +v -0.067500 0.075000 -0.010825 +v -0.073750 0.075000 -0.012500 +v -0.080000 0.075000 -0.010825 +v -0.084575 0.075000 -0.006250 +v -0.086250 0.075000 0.000000 +v -0.084575 0.075000 0.006250 +v -0.080000 0.075000 0.010825 +v -0.073750 0.075000 0.012500 +v -0.067500 0.075000 0.010825 +v -0.062925 0.075000 0.006250 +v -0.061250 0.075000 0.000000 +v -0.021552 0.000426 -0.001618 +v -0.020368 0.000426 -0.002802 +v -0.018750 0.000426 -0.003235 +v -0.017132 0.000426 -0.002802 +v -0.015948 0.000426 -0.001618 +v -0.015515 0.000426 -0.000000 +v -0.015948 0.000426 0.001617 +v -0.017132 0.000426 0.002802 +v -0.018750 0.000426 0.003235 +v -0.020368 0.000426 0.002802 +v -0.021552 0.000426 0.001617 +v -0.021985 0.000426 -0.000000 +v -0.024163 0.001675 -0.003125 +v -0.021875 0.001675 -0.005413 +v -0.018750 0.001675 -0.006250 +v -0.015625 0.001675 -0.005413 +v -0.013337 0.001675 -0.003125 +v -0.012500 0.001675 -0.000000 +v -0.013337 0.001675 0.003125 +v -0.015625 0.001675 0.005412 +v -0.018750 0.001675 0.006250 +v -0.021875 0.001675 0.005412 +v -0.024163 0.001675 0.003125 +v -0.025000 0.001675 -0.000000 +v -0.026405 0.003661 -0.004420 +v -0.023169 0.003661 -0.007655 +v -0.018750 0.003661 -0.008839 +v -0.014331 0.003661 -0.007655 +v -0.011095 0.003661 -0.004420 +v -0.009911 0.003661 -0.000000 +v -0.011095 0.003661 0.004419 +v -0.014331 0.003661 0.007654 +v -0.018750 0.003661 0.008839 +v -0.023169 0.003661 0.007654 +v -0.026405 0.003661 0.004419 +v -0.027589 0.003661 -0.000000 +v -0.028125 0.006250 -0.005413 +v -0.024163 0.006250 -0.009375 +v -0.018750 0.006250 -0.010825 +v -0.013337 0.006250 -0.009375 +v -0.009375 0.006250 -0.005413 +v -0.007925 0.006250 -0.000000 +v -0.009375 0.006250 0.005412 +v -0.013337 0.006250 0.009375 +v -0.018750 0.006250 0.010825 +v -0.024163 0.006250 0.009375 +v -0.028125 0.006250 0.005412 +v -0.029575 0.006250 -0.000000 +v -0.029207 0.009265 -0.006037 +v -0.024787 0.009265 -0.010457 +v -0.018750 0.009265 -0.012074 +v -0.012713 0.009265 -0.010457 +v -0.008294 0.009265 -0.006037 +v -0.006676 0.009265 -0.000000 +v -0.008294 0.009265 0.006037 +v -0.012713 0.009265 0.010456 +v -0.018750 0.009265 0.012074 +v -0.024787 0.009265 0.010456 +v -0.029207 0.009265 0.006037 +v -0.030824 0.009265 -0.000000 +v -0.018750 0.000000 -0.000000 +v -0.076552 0.062926 -0.001618 +v -0.075368 0.062926 -0.002802 +v -0.073750 0.062926 -0.003235 +v -0.072132 0.062926 -0.002802 +v -0.070948 0.062926 -0.001618 +v -0.070515 0.062926 0.000000 +v -0.070948 0.062926 0.001618 +v -0.072132 0.062926 0.002802 +v -0.073750 0.062926 0.003235 +v -0.075368 0.062926 0.002802 +v -0.076552 0.062926 0.001618 +v -0.076985 0.062926 0.000000 +v -0.079163 0.064174 -0.003125 +v -0.076875 0.064174 -0.005413 +v -0.073750 0.064174 -0.006250 +v -0.070625 0.064174 -0.005413 +v -0.068337 0.064174 -0.003125 +v -0.067500 0.064174 0.000000 +v -0.068337 0.064174 0.003125 +v -0.070625 0.064174 0.005413 +v -0.073750 0.064174 0.006250 +v -0.076875 0.064174 0.005413 +v -0.079163 0.064174 0.003125 +v -0.080000 0.064174 0.000000 +v -0.081405 0.066161 -0.004419 +v -0.078170 0.066161 -0.007655 +v -0.073750 0.066161 -0.008839 +v -0.069331 0.066161 -0.007655 +v -0.066095 0.066161 -0.004419 +v -0.064911 0.066161 0.000000 +v -0.066095 0.066161 0.004419 +v -0.069331 0.066161 0.007655 +v -0.073750 0.066161 0.008839 +v -0.078170 0.066161 0.007655 +v -0.081405 0.066161 0.004419 +v -0.082589 0.066161 0.000000 +v -0.083125 0.068750 -0.005413 +v -0.079163 0.068750 -0.009375 +v -0.073750 0.068750 -0.010825 +v -0.068337 0.068750 -0.009375 +v -0.064375 0.068750 -0.005413 +v -0.062925 0.068750 0.000000 +v -0.064375 0.068750 0.005413 +v -0.068337 0.068750 0.009375 +v -0.073750 0.068750 0.010825 +v -0.079163 0.068750 0.009375 +v -0.083125 0.068750 0.005413 +v -0.084575 0.068750 0.000000 +v -0.084207 0.071765 -0.006037 +v -0.079787 0.071765 -0.010456 +v -0.073750 0.071765 -0.012074 +v -0.067713 0.071765 -0.010456 +v -0.063294 0.071765 -0.006037 +v -0.061676 0.071765 0.000000 +v -0.063294 0.071765 0.006037 +v -0.067713 0.071765 0.010456 +v -0.073750 0.071765 0.012074 +v -0.079787 0.071765 0.010456 +v -0.084207 0.071765 0.006037 +v -0.085824 0.071765 0.000000 +v -0.073750 0.062500 0.000000 +v -0.076552 0.137074 -0.001618 +v -0.075368 0.137074 -0.002802 +v -0.073750 0.137074 -0.003235 +v -0.072132 0.137074 -0.002802 +v -0.070948 0.137074 -0.001618 +v -0.070515 0.137074 -0.000000 +v -0.070948 0.137074 0.001618 +v -0.072132 0.137074 0.002802 +v -0.073750 0.137074 0.003235 +v -0.075368 0.137074 0.002802 +v -0.076552 0.137074 0.001618 +v -0.076985 0.137074 -0.000000 +v -0.079163 0.135825 -0.003125 +v -0.076875 0.135825 -0.005413 +v -0.073750 0.135825 -0.006250 +v -0.070625 0.135825 -0.005413 +v -0.068337 0.135825 -0.003125 +v -0.067500 0.135825 -0.000000 +v -0.068337 0.135825 0.003125 +v -0.070625 0.135825 0.005413 +v -0.073750 0.135825 0.006250 +v -0.076875 0.135825 0.005413 +v -0.079163 0.135825 0.003125 +v -0.080000 0.135825 -0.000000 +v -0.081405 0.133839 -0.004419 +v -0.078170 0.133839 -0.007655 +v -0.073750 0.133839 -0.008839 +v -0.069331 0.133839 -0.007655 +v -0.066095 0.133839 -0.004419 +v -0.064911 0.133839 -0.000000 +v -0.066095 0.133839 0.004419 +v -0.069331 0.133839 0.007655 +v -0.073750 0.133839 0.008839 +v -0.078170 0.133839 0.007655 +v -0.081405 0.133839 0.004419 +v -0.082589 0.133839 -0.000000 +v -0.083125 0.131250 -0.005413 +v -0.079163 0.131250 -0.009375 +v -0.073750 0.131250 -0.010825 +v -0.068337 0.131250 -0.009375 +v -0.064375 0.131250 -0.005413 +v -0.062925 0.131250 -0.000000 +v -0.064375 0.131250 0.005413 +v -0.068337 0.131250 0.009375 +v -0.073750 0.131250 0.010825 +v -0.079163 0.131250 0.009375 +v -0.083125 0.131250 0.005413 +v -0.084575 0.131250 -0.000000 +v -0.084207 0.128235 -0.006037 +v -0.079787 0.128235 -0.010456 +v -0.073750 0.128235 -0.012074 +v -0.067713 0.128235 -0.010456 +v -0.063294 0.128235 -0.006037 +v -0.061676 0.128235 -0.000000 +v -0.063294 0.128235 0.006037 +v -0.067713 0.128235 0.010456 +v -0.073750 0.128235 0.012074 +v -0.079787 0.128235 0.010456 +v -0.084207 0.128235 0.006037 +v -0.085824 0.128235 -0.000000 +v -0.073750 0.137500 -0.000000 +v 0.021552 0.000426 -0.001618 +v 0.020368 0.000426 -0.002802 +v 0.018750 0.000426 -0.003235 +v 0.017132 0.000426 -0.002802 +v 0.015948 0.000426 -0.001618 +v 0.015515 0.000426 -0.000000 +v 0.015948 0.000426 0.001617 +v 0.017132 0.000426 0.002802 +v 0.018750 0.000426 0.003235 +v 0.020368 0.000426 0.002802 +v 0.021552 0.000426 0.001617 +v 0.021985 0.000426 -0.000000 +v 0.024163 0.001675 -0.003125 +v 0.021875 0.001675 -0.005413 +v 0.018750 0.001675 -0.006250 +v 0.015625 0.001675 -0.005413 +v 0.013337 0.001675 -0.003125 +v 0.012500 0.001675 -0.000000 +v 0.013337 0.001675 0.003125 +v 0.015625 0.001675 0.005412 +v 0.018750 0.001675 0.006250 +v 0.021875 0.001675 0.005412 +v 0.024163 0.001675 0.003125 +v 0.025000 0.001675 -0.000000 +v 0.026405 0.003661 -0.004420 +v 0.023169 0.003661 -0.007655 +v 0.018750 0.003661 -0.008839 +v 0.014331 0.003661 -0.007655 +v 0.011095 0.003661 -0.004420 +v 0.009911 0.003661 -0.000000 +v 0.011095 0.003661 0.004419 +v 0.014331 0.003661 0.007654 +v 0.018750 0.003661 0.008839 +v 0.023169 0.003661 0.007654 +v 0.026405 0.003661 0.004419 +v 0.027589 0.003661 -0.000000 +v 0.028125 0.006250 -0.005413 +v 0.024163 0.006250 -0.009375 +v 0.018750 0.006250 -0.010825 +v 0.013337 0.006250 -0.009375 +v 0.009375 0.006250 -0.005413 +v 0.007925 0.006250 -0.000000 +v 0.009375 0.006250 0.005412 +v 0.013337 0.006250 0.009375 +v 0.018750 0.006250 0.010825 +v 0.024163 0.006250 0.009375 +v 0.028125 0.006250 0.005412 +v 0.029575 0.006250 -0.000000 +v 0.029207 0.009265 -0.006037 +v 0.024787 0.009265 -0.010457 +v 0.018750 0.009265 -0.012074 +v 0.012713 0.009265 -0.010457 +v 0.008294 0.009265 -0.006037 +v 0.006676 0.009265 -0.000000 +v 0.008294 0.009265 0.006037 +v 0.012713 0.009265 0.010456 +v 0.018750 0.009265 0.012074 +v 0.024787 0.009265 0.010456 +v 0.029207 0.009265 0.006037 +v 0.030824 0.009265 -0.000000 +v 0.018750 0.000000 -0.000000 +v 0.076552 0.062926 -0.001618 +v 0.075368 0.062926 -0.002802 +v 0.073750 0.062926 -0.003235 +v 0.072132 0.062926 -0.002802 +v 0.070948 0.062926 -0.001618 +v 0.070515 0.062926 0.000000 +v 0.070948 0.062926 0.001618 +v 0.072132 0.062926 0.002802 +v 0.073750 0.062926 0.003235 +v 0.075368 0.062926 0.002802 +v 0.076552 0.062926 0.001618 +v 0.076985 0.062926 0.000000 +v 0.079163 0.064174 -0.003125 +v 0.076875 0.064174 -0.005413 +v 0.073750 0.064174 -0.006250 +v 0.070625 0.064174 -0.005413 +v 0.068337 0.064174 -0.003125 +v 0.067500 0.064174 0.000000 +v 0.068337 0.064174 0.003125 +v 0.070625 0.064174 0.005413 +v 0.073750 0.064174 0.006250 +v 0.076875 0.064174 0.005413 +v 0.079163 0.064174 0.003125 +v 0.080000 0.064174 0.000000 +v 0.081405 0.066161 -0.004419 +v 0.078170 0.066161 -0.007655 +v 0.073750 0.066161 -0.008839 +v 0.069331 0.066161 -0.007655 +v 0.066095 0.066161 -0.004419 +v 0.064911 0.066161 0.000000 +v 0.066095 0.066161 0.004419 +v 0.069331 0.066161 0.007655 +v 0.073750 0.066161 0.008839 +v 0.078170 0.066161 0.007655 +v 0.081405 0.066161 0.004419 +v 0.082589 0.066161 0.000000 +v 0.083125 0.068750 -0.005413 +v 0.079163 0.068750 -0.009375 +v 0.073750 0.068750 -0.010825 +v 0.068337 0.068750 -0.009375 +v 0.064375 0.068750 -0.005413 +v 0.062925 0.068750 0.000000 +v 0.064375 0.068750 0.005413 +v 0.068337 0.068750 0.009375 +v 0.073750 0.068750 0.010825 +v 0.079163 0.068750 0.009375 +v 0.083125 0.068750 0.005413 +v 0.084575 0.068750 0.000000 +v 0.084207 0.071765 -0.006037 +v 0.079787 0.071765 -0.010456 +v 0.073750 0.071765 -0.012074 +v 0.067713 0.071765 -0.010456 +v 0.063294 0.071765 -0.006037 +v 0.061676 0.071765 0.000000 +v 0.063294 0.071765 0.006037 +v 0.067713 0.071765 0.010456 +v 0.073750 0.071765 0.012074 +v 0.079787 0.071765 0.010456 +v 0.084207 0.071765 0.006037 +v 0.085824 0.071765 0.000000 +v 0.073750 0.062500 0.000000 +v 0.076552 0.137074 -0.001618 +v 0.075368 0.137074 -0.002802 +v 0.073750 0.137074 -0.003235 +v 0.072132 0.137074 -0.002802 +v 0.070948 0.137074 -0.001618 +v 0.070515 0.137074 -0.000000 +v 0.070948 0.137074 0.001618 +v 0.072132 0.137074 0.002802 +v 0.073750 0.137074 0.003235 +v 0.075368 0.137074 0.002802 +v 0.076552 0.137074 0.001618 +v 0.076985 0.137074 -0.000000 +v 0.079163 0.135825 -0.003125 +v 0.076875 0.135825 -0.005413 +v 0.073750 0.135825 -0.006250 +v 0.070625 0.135825 -0.005413 +v 0.068337 0.135825 -0.003125 +v 0.067500 0.135825 -0.000000 +v 0.068337 0.135825 0.003125 +v 0.070625 0.135825 0.005413 +v 0.073750 0.135825 0.006250 +v 0.076875 0.135825 0.005413 +v 0.079163 0.135825 0.003125 +v 0.080000 0.135825 -0.000000 +v 0.081405 0.133839 -0.004419 +v 0.078170 0.133839 -0.007655 +v 0.073750 0.133839 -0.008839 +v 0.069331 0.133839 -0.007655 +v 0.066095 0.133839 -0.004419 +v 0.064911 0.133839 -0.000000 +v 0.066095 0.133839 0.004419 +v 0.069331 0.133839 0.007655 +v 0.073750 0.133839 0.008839 +v 0.078170 0.133839 0.007655 +v 0.081405 0.133839 0.004419 +v 0.082589 0.133839 -0.000000 +v 0.083125 0.131250 -0.005413 +v 0.079163 0.131250 -0.009375 +v 0.073750 0.131250 -0.010825 +v 0.068337 0.131250 -0.009375 +v 0.064375 0.131250 -0.005413 +v 0.062925 0.131250 -0.000000 +v 0.064375 0.131250 0.005413 +v 0.068337 0.131250 0.009375 +v 0.073750 0.131250 0.010825 +v 0.079163 0.131250 0.009375 +v 0.083125 0.131250 0.005413 +v 0.084575 0.131250 -0.000000 +v 0.084207 0.128235 -0.006037 +v 0.079787 0.128235 -0.010456 +v 0.073750 0.128235 -0.012074 +v 0.067713 0.128235 -0.010456 +v 0.063294 0.128235 -0.006037 +v 0.061676 0.128235 -0.000000 +v 0.063294 0.128235 0.006037 +v 0.067713 0.128235 0.010456 +v 0.073750 0.128235 0.012074 +v 0.079787 0.128235 0.010456 +v 0.084207 0.128235 0.006037 +v 0.085824 0.128235 -0.000000 +v 0.073750 0.137500 -0.000000 +vt 0.055846 0.659882 +vt 0.055846 0.669764 +vt 0.055848 0.679645 +vt 0.055848 0.689527 +vt 0.055847 0.620355 +vt 0.055846 0.630237 +vt 0.055846 0.640118 +vt 0.055846 0.650000 +vt 0.042671 0.659882 +vt 0.042671 0.669764 +vt 0.042671 0.679646 +vt 0.042671 0.610473 +vt 0.042671 0.620355 +vt 0.042671 0.630237 +vt 0.042671 0.640119 +vt 0.042671 0.650000 +vt 0.029495 0.659882 +vt 0.029495 0.669764 +vt 0.029496 0.679646 +vt 0.029495 0.610473 +vt 0.029495 0.620355 +vt 0.029495 0.630237 +vt 0.029495 0.640118 +vt 0.029495 0.650000 +vt 0.016319 0.664823 +vt 0.016319 0.674705 +vt 0.016320 0.684587 +vt 0.016320 0.615414 +vt 0.016319 0.625296 +vt 0.016319 0.635178 +vt 0.016320 0.645059 +vt 0.016319 0.654941 +vt 0.851173 0.793896 +vt 0.856730 0.787051 +vt 0.855527 0.795680 +vt 0.846863 0.795712 +vt 0.842730 0.790247 +vt 0.851181 0.806034 +vt 0.856732 0.812949 +vt 0.842730 0.809753 +vt 0.846869 0.804239 +vt 0.855530 0.804296 +vt 0.857385 0.800000 +vt 0.858189 0.800000 +vt 0.845151 0.800000 +vt 0.843829 0.800000 +vt 0.748827 0.793896 +vt 0.744473 0.795680 +vt 0.743270 0.787051 +vt 0.753137 0.795712 +vt 0.757270 0.790247 +vt 0.748819 0.806034 +vt 0.753131 0.804239 +vt 0.757270 0.809753 +vt 0.743270 0.812949 +vt 0.744470 0.804296 +vt 0.742615 0.800000 +vt 0.741810 0.800000 +vt 0.754849 0.800000 +vt 0.756171 0.800000 +vt 0.418946 0.313941 +vt 0.450000 0.310442 +vt 0.450000 0.318024 +vt 0.420633 0.321332 +vt 0.389448 0.324263 +vt 0.392738 0.331094 +vt 0.362987 0.340889 +vt 0.367715 0.346817 +vt 0.340889 0.362987 +vt 0.346817 0.367715 +vt 0.324263 0.389448 +vt 0.331093 0.392737 +vt 0.313941 0.418945 +vt 0.321333 0.420632 +vt 0.310442 0.450000 +vt 0.318024 0.450000 +vt 0.313941 0.481055 +vt 0.321333 0.479367 +vt 0.324263 0.510552 +vt 0.331094 0.507263 +vt 0.340889 0.537013 +vt 0.346817 0.532286 +vt 0.362987 0.559111 +vt 0.367715 0.553183 +vt 0.389448 0.575737 +vt 0.392737 0.568906 +vt 0.418946 0.586059 +vt 0.420633 0.578667 +vt 0.450000 0.589558 +vt 0.450000 0.581976 +vt 0.372173 0.547592 +vt 0.395841 0.562464 +vt 0.422223 0.571696 +vt 0.450000 0.574825 +vt 0.450000 0.325175 +vt 0.422223 0.328306 +vt 0.395841 0.337537 +vt 0.372173 0.352408 +vt 0.352408 0.372172 +vt 0.337536 0.395841 +vt 0.328304 0.422223 +vt 0.325175 0.450000 +vt 0.328304 0.477775 +vt 0.337536 0.504159 +vt 0.352408 0.527827 +vt 0.376703 0.541912 +vt 0.358088 0.523298 +vt 0.363535 0.518954 +vt 0.381045 0.536466 +vt 0.398992 0.555918 +vt 0.402015 0.549642 +vt 0.423841 0.564613 +vt 0.425390 0.557822 +vt 0.450000 0.567560 +vt 0.450000 0.560594 +vt 0.423840 0.335387 +vt 0.450000 0.332441 +vt 0.450000 0.339404 +vt 0.425391 0.342179 +vt 0.398993 0.344083 +vt 0.402015 0.350358 +vt 0.376703 0.358087 +vt 0.381046 0.363535 +vt 0.358088 0.376703 +vt 0.363533 0.381045 +vt 0.344082 0.398992 +vt 0.350358 0.402015 +vt 0.335387 0.423841 +vt 0.342178 0.425390 +vt 0.332440 0.450000 +vt 0.339406 0.450000 +vt 0.335387 0.476159 +vt 0.342178 0.474609 +vt 0.344082 0.501008 +vt 0.350358 0.497985 +vt 0.481055 0.313941 +vt 0.479367 0.321332 +vt 0.510552 0.324263 +vt 0.507262 0.331094 +vt 0.537013 0.340889 +vt 0.532286 0.346817 +vt 0.559111 0.362987 +vt 0.553183 0.367714 +vt 0.575738 0.389448 +vt 0.568907 0.392737 +vt 0.586059 0.418945 +vt 0.578667 0.420632 +vt 0.589558 0.450000 +vt 0.581977 0.450000 +vt 0.586059 0.481055 +vt 0.578667 0.479367 +vt 0.575738 0.510552 +vt 0.568907 0.507263 +vt 0.559111 0.537013 +vt 0.553183 0.532286 +vt 0.537013 0.559111 +vt 0.532286 0.553183 +vt 0.510552 0.575737 +vt 0.507262 0.568906 +vt 0.481055 0.586059 +vt 0.479368 0.578667 +vt 0.504159 0.562464 +vt 0.527827 0.547592 +vt 0.477777 0.571696 +vt 0.477777 0.328306 +vt 0.504159 0.337537 +vt 0.527827 0.352408 +vt 0.547592 0.372172 +vt 0.562464 0.395841 +vt 0.571696 0.422223 +vt 0.574824 0.450000 +vt 0.571696 0.477775 +vt 0.562464 0.504159 +vt 0.547592 0.527827 +vt 0.518954 0.536466 +vt 0.536467 0.518954 +vt 0.541912 0.523298 +vt 0.523298 0.541912 +vt 0.497985 0.549642 +vt 0.501007 0.555918 +vt 0.474609 0.557822 +vt 0.476160 0.564613 +vt 0.474609 0.342179 +vt 0.476160 0.335387 +vt 0.497986 0.350358 +vt 0.501007 0.344083 +vt 0.518954 0.363535 +vt 0.523298 0.358087 +vt 0.536467 0.381045 +vt 0.541912 0.376703 +vt 0.549641 0.402015 +vt 0.555919 0.398992 +vt 0.557823 0.425390 +vt 0.564612 0.423841 +vt 0.560594 0.450000 +vt 0.567561 0.450000 +vt 0.557823 0.474609 +vt 0.564612 0.476159 +vt 0.549641 0.497985 +vt 0.555918 0.501008 +vt 0.150000 0.450000 +vt 0.150000 0.584317 +vt 0.120111 0.580950 +vt 0.091721 0.571016 +vt 0.066254 0.555014 +vt 0.044987 0.533746 +vt 0.028984 0.508278 +vt 0.019050 0.479889 +vt 0.015683 0.450000 +vt 0.019050 0.420111 +vt 0.028984 0.391721 +vt 0.044987 0.366255 +vt 0.066254 0.344987 +vt 0.091721 0.328984 +vt 0.120111 0.319050 +vt 0.150000 0.315683 +vt 0.118950 0.313961 +vt 0.150000 0.310462 +vt 0.038795 0.032928 +vt 0.071738 0.032928 +vt 0.071738 0.267072 +vt 0.038795 0.267072 +vt 0.104681 0.032928 +vt 0.104681 0.267072 +vt 0.137625 0.032928 +vt 0.137625 0.267072 +vt 0.170567 0.032928 +vt 0.170567 0.267072 +vt 0.203511 0.032928 +vt 0.203511 0.267072 +vt 0.236454 0.032928 +vt 0.236454 0.267072 +vt 0.269397 0.032928 +vt 0.269397 0.267072 +vt 0.302340 0.032928 +vt 0.302340 0.267072 +vt 0.335284 0.032928 +vt 0.335284 0.267072 +vt 0.368226 0.032928 +vt 0.368226 0.267072 +vt 0.401170 0.032928 +vt 0.401170 0.267072 +vt 0.434114 0.032928 +vt 0.434114 0.267072 +vt 0.467056 0.032928 +vt 0.467056 0.267072 +vt 0.500000 0.032928 +vt 0.500000 0.267072 +vt 0.961205 0.032928 +vt 0.961205 0.267072 +vt 0.928263 0.267072 +vt 0.928263 0.032928 +vt 0.895319 0.267072 +vt 0.895319 0.032928 +vt 0.862375 0.267072 +vt 0.862375 0.032928 +vt 0.829432 0.267072 +vt 0.829432 0.032928 +vt 0.796489 0.267072 +vt 0.796489 0.032928 +vt 0.763546 0.267072 +vt 0.763546 0.032928 +vt 0.730603 0.267072 +vt 0.730603 0.032928 +vt 0.697661 0.267072 +vt 0.697661 0.032928 +vt 0.664716 0.267072 +vt 0.664716 0.032928 +vt 0.631774 0.267072 +vt 0.631774 0.032928 +vt 0.598830 0.267072 +vt 0.598830 0.032928 +vt 0.565886 0.267072 +vt 0.565886 0.032928 +vt 0.532944 0.267072 +vt 0.532944 0.032928 +vt 0.179889 0.319050 +vt 0.208279 0.328984 +vt 0.233746 0.344987 +vt 0.255013 0.366255 +vt 0.271016 0.391721 +vt 0.280950 0.420111 +vt 0.284317 0.450000 +vt 0.280950 0.479889 +vt 0.271016 0.508278 +vt 0.255013 0.533746 +vt 0.233746 0.555014 +vt 0.208279 0.571016 +vt 0.179889 0.580950 +vt 0.089456 0.324280 +vt 0.062999 0.340905 +vt 0.040905 0.363000 +vt 0.024280 0.389456 +vt 0.013961 0.418950 +vt 0.010462 0.450000 +vt 0.013961 0.481050 +vt 0.024280 0.510543 +vt 0.040905 0.537001 +vt 0.062999 0.559096 +vt 0.089456 0.575720 +vt 0.118950 0.586039 +vt 0.150000 0.589538 +vt 0.181050 0.586039 +vt 0.181050 0.313961 +vt 0.210544 0.324280 +vt 0.237000 0.340905 +vt 0.259095 0.363000 +vt 0.275720 0.389456 +vt 0.286039 0.418950 +vt 0.289538 0.450000 +vt 0.286039 0.481050 +vt 0.275720 0.510543 +vt 0.259095 0.537001 +vt 0.237000 0.559096 +vt 0.210544 0.575720 +vt 0.800000 0.963545 +vt 0.763608 0.959445 +vt 0.767378 0.942927 +vt 0.800000 0.946603 +vt 0.729040 0.947350 +vt 0.736391 0.932084 +vt 0.698031 0.927865 +vt 0.708595 0.914618 +vt 0.672135 0.901969 +vt 0.685382 0.891405 +vt 0.652651 0.870960 +vt 0.667916 0.863609 +vt 0.640555 0.836393 +vt 0.657073 0.832622 +vt 0.636455 0.800000 +vt 0.653397 0.800000 +vt 0.640555 0.763607 +vt 0.657073 0.767378 +vt 0.652651 0.729040 +vt 0.667916 0.736391 +vt 0.672135 0.698032 +vt 0.685382 0.708595 +vt 0.698031 0.672135 +vt 0.708595 0.685382 +vt 0.729040 0.652650 +vt 0.736391 0.667916 +vt 0.763607 0.640555 +vt 0.767378 0.657073 +vt 0.800000 0.636455 +vt 0.800000 0.653397 +vt 0.770911 0.927450 +vt 0.800000 0.930727 +vt 0.743280 0.917782 +vt 0.718493 0.902207 +vt 0.697793 0.881507 +vt 0.682218 0.856721 +vt 0.672550 0.829089 +vt 0.669273 0.800000 +vt 0.672550 0.770911 +vt 0.682218 0.743280 +vt 0.697793 0.718493 +vt 0.718493 0.697793 +vt 0.743280 0.682218 +vt 0.770911 0.672550 +vt 0.800000 0.669273 +vt 0.774274 0.912713 +vt 0.800000 0.915612 +vt 0.749837 0.904163 +vt 0.727917 0.890389 +vt 0.709611 0.872084 +vt 0.695837 0.850163 +vt 0.687287 0.825726 +vt 0.684388 0.800000 +vt 0.687287 0.774274 +vt 0.695837 0.749838 +vt 0.709611 0.727918 +vt 0.727917 0.709611 +vt 0.749837 0.695837 +vt 0.774274 0.687287 +vt 0.800000 0.684388 +vt 0.777526 0.898465 +vt 0.800000 0.900997 +vt 0.756179 0.890995 +vt 0.737029 0.878962 +vt 0.721038 0.862971 +vt 0.709005 0.843822 +vt 0.701536 0.822474 +vt 0.699003 0.800000 +vt 0.701536 0.777526 +vt 0.709005 0.756179 +vt 0.721038 0.737029 +vt 0.737029 0.721038 +vt 0.756179 0.709005 +vt 0.777526 0.701536 +vt 0.800000 0.699003 +vt 0.780715 0.884493 +vt 0.800000 0.886665 +vt 0.762397 0.878083 +vt 0.745964 0.867758 +vt 0.732242 0.854036 +vt 0.721917 0.837603 +vt 0.715507 0.819285 +vt 0.713335 0.800000 +vt 0.715507 0.780715 +vt 0.721917 0.762398 +vt 0.732242 0.745964 +vt 0.745964 0.732242 +vt 0.762397 0.721917 +vt 0.780715 0.715507 +vt 0.800000 0.713335 +vt 0.783880 0.870625 +vt 0.800000 0.872441 +vt 0.768569 0.865268 +vt 0.754834 0.856637 +vt 0.743363 0.845167 +vt 0.734732 0.831431 +vt 0.729375 0.816120 +vt 0.727560 0.800000 +vt 0.729375 0.783880 +vt 0.734732 0.768569 +vt 0.743363 0.754835 +vt 0.754834 0.743363 +vt 0.768569 0.734732 +vt 0.783880 0.729375 +vt 0.800000 0.727558 +vt 0.787051 0.856731 +vt 0.800000 0.858189 +vt 0.774753 0.852427 +vt 0.763719 0.845495 +vt 0.754505 0.836281 +vt 0.747573 0.825248 +vt 0.747573 0.774753 +vt 0.754505 0.763719 +vt 0.763719 0.754505 +vt 0.774753 0.747573 +vt 0.787051 0.743268 +vt 0.800000 0.741810 +vt 0.790247 0.842730 +vt 0.800000 0.843829 +vt 0.780983 0.839489 +vt 0.772673 0.834267 +vt 0.765733 0.827327 +vt 0.760511 0.819017 +vt 0.760511 0.780983 +vt 0.765733 0.772673 +vt 0.772673 0.765733 +vt 0.780983 0.760511 +vt 0.790247 0.757270 +vt 0.800000 0.756171 +vt 0.793474 0.828592 +vt 0.800000 0.829327 +vt 0.787275 0.826423 +vt 0.781715 0.822929 +vt 0.777071 0.818285 +vt 0.773577 0.812725 +vt 0.771408 0.806526 +vt 0.770673 0.800000 +vt 0.771408 0.793474 +vt 0.773577 0.787275 +vt 0.777071 0.781715 +vt 0.781715 0.777071 +vt 0.787275 0.773577 +vt 0.793474 0.771408 +vt 0.800000 0.770673 +vt 0.796729 0.814331 +vt 0.800000 0.814700 +vt 0.793621 0.813245 +vt 0.790834 0.811493 +vt 0.788507 0.809166 +vt 0.786755 0.806379 +vt 0.785669 0.803271 +vt 0.785300 0.800000 +vt 0.785668 0.796729 +vt 0.786755 0.793622 +vt 0.788507 0.790835 +vt 0.790834 0.788508 +vt 0.793621 0.786755 +vt 0.796729 0.785669 +vt 0.800000 0.785300 +vt 0.800000 0.800000 +vt 0.832622 0.942927 +vt 0.836392 0.959445 +vt 0.863609 0.932084 +vt 0.870960 0.947350 +vt 0.891405 0.914618 +vt 0.901968 0.927865 +vt 0.914618 0.891405 +vt 0.927865 0.901969 +vt 0.932084 0.863608 +vt 0.947350 0.870960 +vt 0.942927 0.832622 +vt 0.959445 0.836393 +vt 0.946603 0.800000 +vt 0.963545 0.800000 +vt 0.942927 0.767378 +vt 0.959445 0.763608 +vt 0.932084 0.736391 +vt 0.947350 0.729040 +vt 0.914618 0.708595 +vt 0.927865 0.698032 +vt 0.891405 0.685382 +vt 0.901968 0.672135 +vt 0.863609 0.667916 +vt 0.870960 0.652650 +vt 0.832622 0.657073 +vt 0.836392 0.640555 +vt 0.829089 0.927450 +vt 0.856721 0.917782 +vt 0.881507 0.902207 +vt 0.902206 0.881507 +vt 0.917782 0.856721 +vt 0.927450 0.829090 +vt 0.930727 0.800000 +vt 0.927450 0.770910 +vt 0.917782 0.743280 +vt 0.902206 0.718493 +vt 0.881507 0.697793 +vt 0.856721 0.682218 +vt 0.829089 0.672550 +vt 0.825726 0.912713 +vt 0.850162 0.904163 +vt 0.872083 0.890389 +vt 0.890389 0.872083 +vt 0.904163 0.850163 +vt 0.912713 0.825726 +vt 0.915612 0.800000 +vt 0.912713 0.774274 +vt 0.904163 0.749838 +vt 0.890389 0.727918 +vt 0.872083 0.709611 +vt 0.850162 0.695837 +vt 0.825726 0.687286 +vt 0.822474 0.898465 +vt 0.843821 0.890995 +vt 0.862971 0.878962 +vt 0.878962 0.862971 +vt 0.890995 0.843822 +vt 0.898464 0.822474 +vt 0.900997 0.800000 +vt 0.898464 0.777526 +vt 0.890995 0.756179 +vt 0.878962 0.737029 +vt 0.862971 0.721038 +vt 0.843821 0.709005 +vt 0.822474 0.701536 +vt 0.819285 0.884493 +vt 0.837603 0.878083 +vt 0.854036 0.867758 +vt 0.867758 0.854036 +vt 0.878083 0.837603 +vt 0.884493 0.819285 +vt 0.886665 0.800000 +vt 0.884493 0.780715 +vt 0.878083 0.762398 +vt 0.867758 0.745964 +vt 0.854036 0.732242 +vt 0.837603 0.721917 +vt 0.819285 0.715507 +vt 0.816120 0.870625 +vt 0.831431 0.865268 +vt 0.845166 0.856637 +vt 0.856637 0.845166 +vt 0.865268 0.831431 +vt 0.870625 0.816120 +vt 0.872442 0.800000 +vt 0.870625 0.783880 +vt 0.865268 0.768569 +vt 0.856637 0.754835 +vt 0.845167 0.743363 +vt 0.831431 0.734732 +vt 0.816120 0.729375 +vt 0.812949 0.856731 +vt 0.825247 0.852427 +vt 0.836281 0.845495 +vt 0.845495 0.836281 +vt 0.852427 0.825247 +vt 0.852427 0.774753 +vt 0.845495 0.763719 +vt 0.836281 0.754505 +vt 0.825248 0.747573 +vt 0.812949 0.743270 +vt 0.809753 0.842730 +vt 0.819017 0.839488 +vt 0.827327 0.834267 +vt 0.834267 0.827327 +vt 0.839489 0.819017 +vt 0.839489 0.780983 +vt 0.834267 0.772673 +vt 0.827327 0.765733 +vt 0.819017 0.760511 +vt 0.809753 0.757270 +vt 0.806526 0.828592 +vt 0.812725 0.826423 +vt 0.818285 0.822929 +vt 0.822929 0.818285 +vt 0.826423 0.812725 +vt 0.828592 0.806526 +vt 0.829327 0.800000 +vt 0.828592 0.793474 +vt 0.826423 0.787275 +vt 0.822929 0.781715 +vt 0.818285 0.777071 +vt 0.812725 0.773577 +vt 0.806526 0.771408 +vt 0.803271 0.814331 +vt 0.806379 0.813245 +vt 0.809166 0.811493 +vt 0.811493 0.809166 +vt 0.813245 0.806379 +vt 0.814332 0.803271 +vt 0.814700 0.800000 +vt 0.814331 0.796729 +vt 0.813245 0.793622 +vt 0.811493 0.790835 +vt 0.809166 0.788508 +vt 0.806377 0.786755 +vt 0.803271 0.785668 +vt 0.760574 0.972734 +vt 0.800000 0.977177 +vt 0.800000 0.983714 +vt 0.759120 0.979109 +vt 0.723126 0.959631 +vt 0.689532 0.938522 +vt 0.661478 0.910468 +vt 0.640369 0.876875 +vt 0.627266 0.839426 +vt 0.622823 0.800000 +vt 0.627266 0.760574 +vt 0.640369 0.723127 +vt 0.661478 0.689532 +vt 0.689532 0.661478 +vt 0.723125 0.640370 +vt 0.760574 0.627266 +vt 0.800000 0.622823 +vt 0.719984 0.318489 +vt 0.750000 0.315109 +vt 0.750000 0.450000 +vt 0.691473 0.328466 +vt 0.665897 0.344538 +vt 0.644538 0.365896 +vt 0.628466 0.391473 +vt 0.618491 0.419984 +vt 0.615109 0.450000 +vt 0.618491 0.480016 +vt 0.628466 0.508527 +vt 0.644538 0.534103 +vt 0.665897 0.555462 +vt 0.691473 0.571534 +vt 0.719984 0.581509 +vt 0.750000 0.584891 +vt 0.839426 0.972735 +vt 0.876875 0.959631 +vt 0.910468 0.938522 +vt 0.938522 0.910468 +vt 0.959631 0.876875 +vt 0.972734 0.839426 +vt 0.977178 0.800000 +vt 0.972736 0.760574 +vt 0.959631 0.723127 +vt 0.938522 0.689532 +vt 0.910468 0.661478 +vt 0.876875 0.640370 +vt 0.839426 0.627265 +vt 0.720289 0.965521 +vt 0.685457 0.943634 +vt 0.656366 0.914545 +vt 0.634480 0.879710 +vt 0.620892 0.840881 +vt 0.616286 0.800000 +vt 0.620892 0.759120 +vt 0.634480 0.720289 +vt 0.656366 0.685456 +vt 0.685457 0.656366 +vt 0.720289 0.634479 +vt 0.759120 0.620892 +vt 0.800000 0.616286 +vt 0.840880 0.620892 +vt 0.840880 0.979108 +vt 0.879711 0.965521 +vt 0.914544 0.943634 +vt 0.943634 0.914545 +vt 0.965520 0.879710 +vt 0.979108 0.840881 +vt 0.983714 0.800000 +vt 0.979108 0.759120 +vt 0.965522 0.720289 +vt 0.943634 0.685456 +vt 0.914544 0.656366 +vt 0.879711 0.634479 +vt 0.780016 0.318491 +vt 0.808527 0.328466 +vt 0.834103 0.344538 +vt 0.855462 0.365896 +vt 0.871533 0.391473 +vt 0.881509 0.419984 +vt 0.884891 0.450000 +vt 0.881509 0.480016 +vt 0.871533 0.508527 +vt 0.855462 0.534103 +vt 0.834103 0.555462 +vt 0.808527 0.571534 +vt 0.780016 0.581509 +vt 0.346499 0.450000 +vt 0.349094 0.473031 +vt 0.356748 0.494908 +vt 0.369079 0.514533 +vt 0.385469 0.530921 +vt 0.405092 0.543252 +vt 0.426970 0.550906 +vt 0.450000 0.553501 +vt 0.473031 0.550906 +vt 0.494908 0.543252 +vt 0.514531 0.530921 +vt 0.530921 0.514533 +vt 0.543252 0.494908 +vt 0.550907 0.473031 +vt 0.553501 0.450000 +vt 0.550907 0.426969 +vt 0.543252 0.405092 +vt 0.530920 0.385468 +vt 0.514533 0.369079 +vt 0.494908 0.356749 +vt 0.473031 0.349092 +vt 0.450000 0.346500 +vt 0.426969 0.349092 +vt 0.405092 0.356749 +vt 0.385467 0.369079 +vt 0.369080 0.385468 +vt 0.356748 0.405092 +vt 0.349094 0.426969 +vt 0.377697 0.450000 +vt 0.381758 0.465156 +vt 0.392853 0.476252 +vt 0.408010 0.480313 +vt 0.423166 0.476252 +vt 0.434261 0.465156 +vt 0.438322 0.450000 +vt 0.450000 0.450000 +vt 0.434261 0.434844 +vt 0.423166 0.423748 +vt 0.408010 0.419687 +vt 0.392853 0.423747 +vt 0.381758 0.434844 +vt 0.522303 0.450000 +vt 0.518242 0.465156 +vt 0.507147 0.476252 +vt 0.491990 0.480313 +vt 0.476834 0.476252 +vt 0.465739 0.465156 +vt 0.461678 0.450000 +vt 0.465739 0.434844 +vt 0.476834 0.423748 +vt 0.491990 0.419687 +vt 0.507147 0.423747 +vt 0.518242 0.434844 +vt 0.176121 0.669763 +vt 0.176121 0.679645 +vt 0.176121 0.659881 +vt 0.176120 0.650000 +vt 0.176120 0.640118 +vt 0.176120 0.630236 +vt 0.176120 0.620354 +vt 0.055847 0.610473 +vt 0.176120 0.610473 +vt 0.176121 0.689527 +vt 0.473387 0.972703 +vt 0.473387 0.962787 +vt 0.486693 0.962787 +vt 0.486693 0.972703 +vt 0.460080 0.972703 +vt 0.460080 0.962787 +vt 0.446774 0.972702 +vt 0.446774 0.962787 +vt 0.433468 0.972703 +vt 0.433468 0.962787 +vt 0.420161 0.972702 +vt 0.420161 0.962787 +vt 0.500000 0.962787 +vt 0.500000 0.972703 +vt 0.566532 0.972703 +vt 0.566532 0.962787 +vt 0.579839 0.962787 +vt 0.579839 0.972703 +vt 0.553226 0.972703 +vt 0.553226 0.962787 +vt 0.539919 0.972703 +vt 0.539919 0.962787 +vt 0.526613 0.972703 +vt 0.526613 0.962787 +vt 0.513306 0.972703 +vt 0.513306 0.962787 +vt 0.153226 0.898485 +vt 0.153226 0.801515 +vt 0.166532 0.801515 +vt 0.166533 0.898485 +vt 0.139920 0.898485 +vt 0.139919 0.801515 +vt 0.126613 0.898485 +vt 0.126613 0.801515 +vt 0.113307 0.898485 +vt 0.113306 0.801515 +vt 0.100000 0.898485 +vt 0.100000 0.801515 +vt 0.086694 0.898485 +vt 0.086693 0.801515 +vt 0.073387 0.898485 +vt 0.073387 0.801515 +vt 0.060081 0.898485 +vt 0.060080 0.801515 +vt 0.046775 0.898485 +vt 0.046774 0.801516 +vt 0.033468 0.898485 +vt 0.033468 0.801516 +vt 0.020162 0.898485 +vt 0.020161 0.801516 +vt 0.179838 0.801515 +vt 0.179839 0.898485 +vt 0.183680 0.620354 +vt 0.183681 0.630236 +vt 0.183680 0.610473 +vt 0.183681 0.679645 +vt 0.183681 0.689527 +vt 0.183681 0.669763 +vt 0.183681 0.659881 +vt 0.183681 0.650000 +vt 0.183681 0.640118 +vt 0.460081 0.907136 +vt 0.473387 0.907136 +vt 0.446774 0.907136 +vt 0.433468 0.907136 +vt 0.420161 0.907136 +vt 0.566532 0.907136 +vt 0.579839 0.907136 +vt 0.553226 0.907136 +vt 0.539919 0.907136 +vt 0.526613 0.907136 +vt 0.513306 0.907136 +vt 0.500000 0.907136 +vt 0.486694 0.907136 +vt 0.344153 0.669764 +vt 0.344154 0.679646 +vt 0.223879 0.679645 +vt 0.223879 0.669763 +vt 0.344153 0.659882 +vt 0.223879 0.659881 +vt 0.344153 0.650000 +vt 0.223879 0.650000 +vt 0.344153 0.640119 +vt 0.223879 0.640118 +vt 0.223879 0.630236 +vt 0.344153 0.630237 +vt 0.344153 0.620355 +vt 0.223879 0.620354 +vt 0.344152 0.610473 +vt 0.223879 0.610473 +vt 0.344154 0.689528 +vt 0.223879 0.689527 +vt 0.526613 0.772703 +vt 0.513306 0.772703 +vt 0.513306 0.762787 +vt 0.526613 0.762787 +vt 0.539919 0.772702 +vt 0.539919 0.762787 +vt 0.553226 0.772703 +vt 0.553226 0.762787 +vt 0.566532 0.772702 +vt 0.566532 0.762787 +vt 0.579839 0.772702 +vt 0.579839 0.762787 +vt 0.500000 0.772703 +vt 0.500000 0.762787 +vt 0.433468 0.772703 +vt 0.420161 0.772703 +vt 0.420161 0.762787 +vt 0.433468 0.762787 +vt 0.446774 0.772703 +vt 0.446774 0.762787 +vt 0.460081 0.772703 +vt 0.460081 0.762787 +vt 0.473387 0.772703 +vt 0.473387 0.762787 +vt 0.486694 0.772703 +vt 0.486694 0.762787 +vt 0.216319 0.630236 +vt 0.216319 0.620354 +vt 0.216319 0.610472 +vt 0.216319 0.689527 +vt 0.216319 0.679645 +vt 0.216319 0.669763 +vt 0.216319 0.659881 +vt 0.216319 0.650000 +vt 0.216319 0.640118 +vt 0.539919 0.707136 +vt 0.526613 0.707136 +vt 0.553226 0.707136 +vt 0.566532 0.707136 +vt 0.579839 0.707136 +vt 0.433468 0.707136 +vt 0.420161 0.707136 +vt 0.446774 0.707136 +vt 0.460081 0.707136 +vt 0.473387 0.707136 +vt 0.486694 0.707136 +vt 0.500000 0.707136 +vt 0.513306 0.707136 +vt 0.357329 0.669764 +vt 0.357329 0.659882 +vt 0.357329 0.679646 +vt 0.357330 0.689528 +vt 0.357329 0.620355 +vt 0.357329 0.630237 +vt 0.357329 0.640119 +vt 0.357329 0.650000 +vt 0.370505 0.669763 +vt 0.370504 0.659882 +vt 0.370505 0.679645 +vt 0.370505 0.689527 +vt 0.370504 0.620355 +vt 0.370504 0.630237 +vt 0.370504 0.640119 +vt 0.370504 0.650000 +vt 0.383681 0.664822 +vt 0.383681 0.674704 +vt 0.383681 0.684586 +vt 0.383680 0.615414 +vt 0.383680 0.625296 +vt 0.383680 0.635178 +vt 0.383680 0.645059 +vt 0.383680 0.654941 +vt 0.357329 0.610473 +vt 0.370504 0.610473 +vt 0.042671 0.689527 +vt 0.029496 0.689527 +vt 0.246774 0.898485 +vt 0.233467 0.898485 +vt 0.233468 0.801515 +vt 0.246774 0.801515 +vt 0.260080 0.898485 +vt 0.260081 0.801515 +vt 0.273387 0.898485 +vt 0.273387 0.801515 +vt 0.286693 0.898485 +vt 0.286694 0.801515 +vt 0.300000 0.898485 +vt 0.300000 0.801515 +vt 0.313306 0.898485 +vt 0.313306 0.801515 +vt 0.326612 0.898485 +vt 0.326613 0.801515 +vt 0.339919 0.898485 +vt 0.339919 0.801516 +vt 0.353225 0.898485 +vt 0.353226 0.801516 +vt 0.366532 0.898485 +vt 0.366532 0.801516 +vt 0.379838 0.898485 +vt 0.379839 0.801516 +vt 0.220161 0.898485 +vt 0.220161 0.801515 +vt 0.486693 0.640604 +vt 0.486694 0.653910 +vt 0.473387 0.653910 +vt 0.473387 0.640604 +vt 0.460081 0.653910 +vt 0.460081 0.640604 +vt 0.446774 0.653910 +vt 0.446774 0.640604 +vt 0.433468 0.653910 +vt 0.433468 0.640604 +vt 0.420161 0.653910 +vt 0.420161 0.640604 +vt 0.579839 0.640604 +vt 0.579839 0.653910 +vt 0.566532 0.653910 +vt 0.566532 0.640604 +vt 0.553226 0.653910 +vt 0.553226 0.640604 +vt 0.539919 0.653910 +vt 0.539919 0.640604 +vt 0.526613 0.653910 +vt 0.526613 0.640604 +vt 0.513306 0.653910 +vt 0.513306 0.640604 +vt 0.500000 0.653910 +vt 0.500000 0.640604 +vt 0.486694 0.667217 +vt 0.473387 0.667217 +vt 0.460081 0.667217 +vt 0.446774 0.667217 +vt 0.433468 0.667217 +vt 0.420161 0.667217 +vt 0.579839 0.667217 +vt 0.566532 0.667217 +vt 0.553226 0.667217 +vt 0.539919 0.667217 +vt 0.526613 0.667217 +vt 0.513306 0.667217 +vt 0.500000 0.667217 +vt 0.486694 0.680523 +vt 0.473387 0.680523 +vt 0.460081 0.680523 +vt 0.446774 0.680523 +vt 0.433468 0.680523 +vt 0.420161 0.680523 +vt 0.579839 0.680523 +vt 0.566532 0.680523 +vt 0.553226 0.680523 +vt 0.539919 0.680523 +vt 0.526613 0.680523 +vt 0.513306 0.680523 +vt 0.500000 0.680523 +vt 0.486694 0.693830 +vt 0.473387 0.693830 +vt 0.460081 0.693830 +vt 0.446774 0.693830 +vt 0.433468 0.693830 +vt 0.420161 0.693830 +vt 0.579839 0.693830 +vt 0.566532 0.693830 +vt 0.553226 0.693830 +vt 0.539919 0.693830 +vt 0.526613 0.693830 +vt 0.513306 0.693830 +vt 0.500000 0.693830 +vt 0.480040 0.627297 +vt 0.466734 0.627297 +vt 0.453427 0.627298 +vt 0.440121 0.627297 +vt 0.426814 0.627298 +vt 0.573185 0.627297 +vt 0.559879 0.627297 +vt 0.546573 0.627297 +vt 0.533266 0.627297 +vt 0.519960 0.627297 +vt 0.506653 0.627297 +vt 0.493347 0.627297 +vt 0.286694 0.734983 +vt 0.286694 0.748289 +vt 0.273387 0.748289 +vt 0.273387 0.734983 +vt 0.260081 0.748289 +vt 0.260081 0.734983 +vt 0.246775 0.748289 +vt 0.246775 0.734983 +vt 0.233468 0.748289 +vt 0.233468 0.734983 +vt 0.220162 0.748289 +vt 0.220162 0.734983 +vt 0.379839 0.734984 +vt 0.379839 0.748290 +vt 0.366533 0.748290 +vt 0.366533 0.734984 +vt 0.353226 0.748290 +vt 0.353226 0.734983 +vt 0.339920 0.748290 +vt 0.339920 0.734983 +vt 0.326613 0.748290 +vt 0.326614 0.734983 +vt 0.313307 0.748289 +vt 0.313307 0.734983 +vt 0.300000 0.748289 +vt 0.300001 0.734983 +vt 0.286694 0.761596 +vt 0.273387 0.761596 +vt 0.260081 0.761596 +vt 0.246774 0.761596 +vt 0.233468 0.761596 +vt 0.220162 0.761595 +vt 0.379839 0.761597 +vt 0.366533 0.761596 +vt 0.353226 0.761596 +vt 0.339920 0.761596 +vt 0.326613 0.761596 +vt 0.313307 0.761596 +vt 0.300000 0.761596 +vt 0.286694 0.774902 +vt 0.273387 0.774902 +vt 0.260081 0.774902 +vt 0.246775 0.774902 +vt 0.233468 0.774902 +vt 0.220162 0.774902 +vt 0.379839 0.774903 +vt 0.366533 0.774903 +vt 0.353226 0.774903 +vt 0.339920 0.774903 +vt 0.326613 0.774903 +vt 0.313307 0.774902 +vt 0.300000 0.774902 +vt 0.286694 0.788209 +vt 0.273387 0.788209 +vt 0.260081 0.788209 +vt 0.246774 0.788208 +vt 0.233468 0.788208 +vt 0.220162 0.788208 +vt 0.379839 0.788209 +vt 0.366533 0.788209 +vt 0.353226 0.788209 +vt 0.339920 0.788209 +vt 0.326613 0.788209 +vt 0.313307 0.788209 +vt 0.300000 0.788209 +vt 0.280041 0.721676 +vt 0.266734 0.721676 +vt 0.253428 0.721676 +vt 0.240121 0.721676 +vt 0.226815 0.721676 +vt 0.373186 0.721677 +vt 0.359880 0.721677 +vt 0.346573 0.721677 +vt 0.333267 0.721677 +vt 0.319960 0.721677 +vt 0.306654 0.721677 +vt 0.293347 0.721676 +vt 0.286693 0.965017 +vt 0.273386 0.965017 +vt 0.273386 0.951711 +vt 0.286693 0.951711 +vt 0.260080 0.965017 +vt 0.260080 0.951711 +vt 0.246773 0.965017 +vt 0.246774 0.951711 +vt 0.233467 0.965017 +vt 0.233467 0.951711 +vt 0.220161 0.965017 +vt 0.220161 0.951711 +vt 0.379838 0.965017 +vt 0.366532 0.965017 +vt 0.366532 0.951711 +vt 0.379838 0.951711 +vt 0.353225 0.965017 +vt 0.353225 0.951711 +vt 0.339919 0.965017 +vt 0.339919 0.951711 +vt 0.326613 0.965017 +vt 0.326613 0.951711 +vt 0.313306 0.965017 +vt 0.313306 0.951711 +vt 0.300000 0.965017 +vt 0.300000 0.951711 +vt 0.273387 0.938405 +vt 0.286693 0.938405 +vt 0.260080 0.938404 +vt 0.246774 0.938404 +vt 0.233467 0.938404 +vt 0.220161 0.938404 +vt 0.366532 0.938405 +vt 0.379838 0.938405 +vt 0.353225 0.938405 +vt 0.339919 0.938405 +vt 0.326612 0.938405 +vt 0.313306 0.938405 +vt 0.300000 0.938405 +vt 0.273387 0.925098 +vt 0.286693 0.925098 +vt 0.260080 0.925098 +vt 0.246774 0.925098 +vt 0.233467 0.925098 +vt 0.220161 0.925098 +vt 0.366532 0.925098 +vt 0.379838 0.925098 +vt 0.353225 0.925098 +vt 0.339919 0.925098 +vt 0.326613 0.925098 +vt 0.313306 0.925098 +vt 0.300000 0.925098 +vt 0.273387 0.911792 +vt 0.286693 0.911792 +vt 0.260080 0.911792 +vt 0.246774 0.911791 +vt 0.233467 0.911791 +vt 0.220161 0.911791 +vt 0.366532 0.911792 +vt 0.379838 0.911792 +vt 0.353225 0.911792 +vt 0.339919 0.911792 +vt 0.326612 0.911792 +vt 0.313306 0.911792 +vt 0.300000 0.911792 +vt 0.280039 0.978324 +vt 0.266733 0.978324 +vt 0.253427 0.978324 +vt 0.240120 0.978324 +vt 0.226814 0.978324 +vt 0.373185 0.978324 +vt 0.359879 0.978324 +vt 0.346572 0.978324 +vt 0.333266 0.978324 +vt 0.319959 0.978324 +vt 0.306653 0.978324 +vt 0.293346 0.978324 +vt 0.513307 0.840604 +vt 0.526613 0.840604 +vt 0.526613 0.853910 +vt 0.513307 0.853910 +vt 0.539920 0.840604 +vt 0.539919 0.853910 +vt 0.553226 0.840604 +vt 0.553226 0.853910 +vt 0.566533 0.840604 +vt 0.566532 0.853911 +vt 0.579839 0.840604 +vt 0.579839 0.853911 +vt 0.420161 0.840604 +vt 0.433468 0.840604 +vt 0.433468 0.853910 +vt 0.420161 0.853910 +vt 0.446774 0.840604 +vt 0.446774 0.853910 +vt 0.460081 0.840604 +vt 0.460081 0.853910 +vt 0.473387 0.840604 +vt 0.473387 0.853910 +vt 0.486694 0.840604 +vt 0.486694 0.853910 +vt 0.500000 0.840604 +vt 0.500000 0.853910 +vt 0.526613 0.867217 +vt 0.513307 0.867217 +vt 0.539919 0.867217 +vt 0.553226 0.867217 +vt 0.566532 0.867217 +vt 0.579839 0.867217 +vt 0.433468 0.867217 +vt 0.420161 0.867217 +vt 0.446774 0.867217 +vt 0.460081 0.867217 +vt 0.473387 0.867217 +vt 0.486694 0.867217 +vt 0.500000 0.867217 +vt 0.526613 0.880523 +vt 0.513307 0.880523 +vt 0.539919 0.880523 +vt 0.553226 0.880523 +vt 0.566532 0.880523 +vt 0.579839 0.880523 +vt 0.433468 0.880523 +vt 0.420161 0.880523 +vt 0.446774 0.880523 +vt 0.460081 0.880523 +vt 0.473387 0.880523 +vt 0.486694 0.880523 +vt 0.500000 0.880523 +vt 0.526613 0.893830 +vt 0.513307 0.893830 +vt 0.539919 0.893830 +vt 0.553226 0.893830 +vt 0.566532 0.893830 +vt 0.579839 0.893830 +vt 0.433468 0.893830 +vt 0.420161 0.893830 +vt 0.446774 0.893830 +vt 0.460081 0.893830 +vt 0.473387 0.893830 +vt 0.486694 0.893830 +vt 0.500000 0.893830 +vt 0.519960 0.827297 +vt 0.533266 0.827297 +vt 0.546573 0.827297 +vt 0.559879 0.827298 +vt 0.573186 0.827298 +vt 0.426814 0.827297 +vt 0.440121 0.827297 +vt 0.453427 0.827297 +vt 0.466734 0.827297 +vt 0.480040 0.827297 +vt 0.493347 0.827297 +vt 0.506653 0.827297 +vt 0.113306 0.734983 +vt 0.126613 0.734983 +vt 0.126613 0.748289 +vt 0.113306 0.748289 +vt 0.139919 0.734983 +vt 0.139919 0.748289 +vt 0.153225 0.734983 +vt 0.153225 0.748289 +vt 0.166532 0.734983 +vt 0.166532 0.748289 +vt 0.179838 0.734983 +vt 0.179838 0.748289 +vt 0.020161 0.734983 +vt 0.033467 0.734983 +vt 0.033467 0.748290 +vt 0.020161 0.748290 +vt 0.046774 0.734983 +vt 0.046774 0.748290 +vt 0.060080 0.734983 +vt 0.060080 0.748290 +vt 0.073387 0.734983 +vt 0.073387 0.748289 +vt 0.086693 0.734983 +vt 0.086693 0.748289 +vt 0.100000 0.734983 +vt 0.100000 0.748289 +vt 0.126613 0.761596 +vt 0.113306 0.761596 +vt 0.139919 0.761596 +vt 0.153225 0.761596 +vt 0.166532 0.761596 +vt 0.179838 0.761596 +vt 0.033467 0.761596 +vt 0.020161 0.761596 +vt 0.046774 0.761596 +vt 0.060080 0.761596 +vt 0.073387 0.761596 +vt 0.086693 0.761596 +vt 0.100000 0.761596 +vt 0.126613 0.774902 +vt 0.113306 0.774902 +vt 0.139919 0.774902 +vt 0.153226 0.774902 +vt 0.166532 0.774902 +vt 0.179838 0.774902 +vt 0.033467 0.774903 +vt 0.020161 0.774903 +vt 0.046774 0.774903 +vt 0.060080 0.774903 +vt 0.073387 0.774902 +vt 0.086693 0.774902 +vt 0.100000 0.774902 +vt 0.126613 0.788209 +vt 0.113306 0.788209 +vt 0.139919 0.788209 +vt 0.153225 0.788209 +vt 0.166532 0.788209 +vt 0.179838 0.788209 +vt 0.033467 0.788209 +vt 0.020161 0.788209 +vt 0.046774 0.788209 +vt 0.060080 0.788209 +vt 0.073387 0.788209 +vt 0.086693 0.788209 +vt 0.100000 0.788209 +vt 0.119959 0.721676 +vt 0.133266 0.721676 +vt 0.146572 0.721676 +vt 0.159879 0.721676 +vt 0.173185 0.721676 +vt 0.026814 0.721677 +vt 0.040120 0.721677 +vt 0.053427 0.721677 +vt 0.066733 0.721677 +vt 0.080040 0.721677 +vt 0.093346 0.721676 +vt 0.106653 0.721676 +vt 0.113307 0.965017 +vt 0.113307 0.951711 +vt 0.126613 0.951711 +vt 0.126614 0.965017 +vt 0.139920 0.951711 +vt 0.139920 0.965017 +vt 0.153226 0.951711 +vt 0.153226 0.965017 +vt 0.166533 0.951711 +vt 0.166533 0.965017 +vt 0.179839 0.951711 +vt 0.179839 0.965017 +vt 0.020162 0.965017 +vt 0.020162 0.951711 +vt 0.033468 0.951711 +vt 0.033468 0.965017 +vt 0.046774 0.951711 +vt 0.046775 0.965017 +vt 0.060081 0.951711 +vt 0.060081 0.965017 +vt 0.073387 0.951711 +vt 0.073387 0.965017 +vt 0.086694 0.951711 +vt 0.086694 0.965017 +vt 0.100000 0.951711 +vt 0.100000 0.965017 +vt 0.113307 0.938404 +vt 0.126613 0.938404 +vt 0.139920 0.938404 +vt 0.153226 0.938404 +vt 0.166533 0.938404 +vt 0.179839 0.938404 +vt 0.020162 0.938404 +vt 0.033468 0.938404 +vt 0.046774 0.938404 +vt 0.060081 0.938404 +vt 0.073387 0.938404 +vt 0.086694 0.938404 +vt 0.100000 0.938404 +vt 0.113307 0.925098 +vt 0.126613 0.925098 +vt 0.139920 0.925098 +vt 0.153226 0.925098 +vt 0.166533 0.925098 +vt 0.179839 0.925098 +vt 0.020162 0.925098 +vt 0.033468 0.925098 +vt 0.046775 0.925098 +vt 0.060081 0.925098 +vt 0.073387 0.925098 +vt 0.086694 0.925098 +vt 0.100000 0.925098 +vt 0.113307 0.911791 +vt 0.126613 0.911792 +vt 0.139920 0.911791 +vt 0.153226 0.911791 +vt 0.166533 0.911791 +vt 0.179839 0.911791 +vt 0.020162 0.911792 +vt 0.033468 0.911791 +vt 0.046775 0.911792 +vt 0.060081 0.911791 +vt 0.073387 0.911792 +vt 0.086694 0.911791 +vt 0.100000 0.911791 +vt 0.119960 0.978324 +vt 0.133267 0.978324 +vt 0.146573 0.978324 +vt 0.159880 0.978324 +vt 0.173186 0.978324 +vt 0.026815 0.978324 +vt 0.040121 0.978324 +vt 0.053428 0.978324 +vt 0.066734 0.978324 +vt 0.080041 0.978324 +vt 0.093347 0.978324 +vt 0.106654 0.978324 +vn 0.618018 -0.340438 -0.708629 +vn 0.004816 0.013405 -0.999899 +vn 0.217515 0.376643 -0.900459 +vn 0.768904 0.058373 -0.636693 +vn -0.606408 0.366126 -0.705848 +vn -0.333953 0.694994 -0.636757 +vn -0.859035 0.511917 -0.000001 +vn -0.562362 0.826891 0.000000 +vn -0.606408 0.366126 0.705848 +vn -0.333953 0.694991 0.636759 +vn 0.004812 0.013407 0.999899 +vn 0.217514 0.376640 0.900461 +vn 0.618018 -0.340438 0.708629 +vn 0.768903 0.058373 0.636695 +vn 0.873073 -0.487589 0.000000 +vn 0.997294 -0.073511 0.000000 +vn 0.386277 0.669203 -0.634790 +vn 0.775055 0.444810 -0.448815 +vn -0.002361 0.893545 -0.448966 +vn -0.163222 0.986589 -0.000002 +vn -0.002361 0.893546 0.448965 +vn 0.386277 0.669201 0.634791 +vn 0.775054 0.444809 0.448817 +vn 0.936026 0.351932 0.000001 +vn 0.500086 0.865976 -0.000003 +vn 0.330416 0.939272 0.092697 +vn 0.396301 0.912607 0.100466 +vn 0.383211 0.922561 0.045064 +vn 0.287051 0.954669 0.078798 +vn 0.269162 0.959884 0.078574 +vn 0.330417 0.939272 -0.092697 +vn 0.396301 0.912607 -0.100466 +vn 0.269162 0.959884 -0.078575 +vn 0.287051 0.954669 -0.078798 +vn 0.383211 0.922561 -0.045061 +vn 0.388183 0.921582 0.000002 +vn 0.436573 0.899669 0.000001 +vn 0.202745 0.979232 0.000000 +vn 0.226299 0.974058 -0.000001 +vn -0.330416 0.939272 0.092697 +vn -0.383210 0.922561 0.045064 +vn -0.396301 0.912607 0.100466 +vn -0.287051 0.954669 0.078798 +vn -0.269162 0.959884 0.078574 +vn -0.330416 0.939272 -0.092702 +vn -0.287051 0.954669 -0.078800 +vn -0.269162 0.959884 -0.078576 +vn -0.396301 0.912607 -0.100467 +vn -0.383212 0.922561 -0.045065 +vn -0.388183 0.921582 0.000000 +vn -0.436572 0.899669 0.000001 +vn -0.202745 0.979232 0.000004 +vn -0.226299 0.974058 0.000001 +vn -0.222517 -0.006117 -0.974910 +vn 0.000000 -0.006117 -0.999981 +vn 0.000000 -0.296295 -0.955097 +vn -0.212529 -0.296295 -0.931150 +vn -0.433876 -0.006117 -0.900952 +vn -0.414401 -0.296294 -0.860513 +vn -0.623478 -0.006117 -0.781817 +vn -0.595493 -0.296294 -0.746725 +vn -0.781817 -0.006117 -0.623478 +vn -0.746725 -0.296294 -0.595493 +vn -0.900952 -0.006117 -0.433876 +vn -0.860512 -0.296295 -0.414401 +vn -0.974910 -0.006117 -0.222517 +vn -0.931150 -0.296294 -0.212530 +vn -0.999981 -0.006117 0.000000 +vn -0.955097 -0.296294 0.000000 +vn -0.974910 -0.006117 0.222517 +vn -0.931150 -0.296295 0.212529 +vn -0.900952 -0.006117 0.433876 +vn -0.860513 -0.296294 0.414401 +vn -0.781817 -0.006117 0.623478 +vn -0.746725 -0.296294 0.595493 +vn -0.623478 -0.006117 0.781817 +vn -0.595493 -0.296294 0.746725 +vn -0.433875 -0.006117 0.900952 +vn -0.414401 -0.296294 0.860513 +vn -0.222517 -0.006117 0.974910 +vn -0.212529 -0.296294 0.931150 +vn 0.000000 -0.006117 0.999981 +vn 0.000000 -0.296294 0.955097 +vn -0.510013 -0.575221 0.639536 +vn -0.354916 -0.575221 0.736991 +vn -0.182022 -0.575221 0.797489 +vn 0.000000 -0.575221 0.817998 +vn 0.000000 -0.575221 -0.817998 +vn -0.182022 -0.575221 -0.797489 +vn -0.354916 -0.575221 -0.736991 +vn -0.510013 -0.575221 -0.639537 +vn -0.639537 -0.575221 -0.510013 +vn -0.736991 -0.575221 -0.354916 +vn -0.797490 -0.575220 -0.182022 +vn -0.817999 -0.575220 0.000000 +vn -0.797489 -0.575221 0.182022 +vn -0.736991 -0.575221 0.354916 +vn -0.639537 -0.575221 0.510013 +vn -0.369762 -0.805164 0.463668 +vn -0.463668 -0.805164 0.369762 +vn -0.253177 -0.946117 0.201902 +vn -0.201902 -0.946117 0.253177 +vn -0.257316 -0.805164 0.534322 +vn -0.140502 -0.946117 0.291757 +vn -0.131967 -0.805164 0.578184 +vn -0.072058 -0.946117 0.315706 +vn 0.000000 -0.805163 0.593053 +vn 0.000000 -0.946117 0.323825 +vn -0.131967 -0.805163 -0.578184 +vn 0.000000 -0.805163 -0.593053 +vn 0.000000 -0.946117 -0.323826 +vn -0.072058 -0.946117 -0.315707 +vn -0.257316 -0.805164 -0.534322 +vn -0.140503 -0.946117 -0.291757 +vn -0.369763 -0.805164 -0.463667 +vn -0.201902 -0.946117 -0.253178 +vn -0.463667 -0.805164 -0.369762 +vn -0.253177 -0.946117 -0.201902 +vn -0.534322 -0.805164 -0.257316 +vn -0.291757 -0.946117 -0.140503 +vn -0.578184 -0.805163 -0.131967 +vn -0.315706 -0.946117 -0.072058 +vn -0.593054 -0.805163 0.000000 +vn -0.323827 -0.946116 0.000000 +vn -0.578184 -0.805163 0.131967 +vn -0.315710 -0.946116 0.072059 +vn -0.534323 -0.805163 0.257316 +vn -0.291758 -0.946116 0.140504 +vn 0.222517 -0.006117 -0.974910 +vn 0.212529 -0.296295 -0.931150 +vn 0.433876 -0.006117 -0.900952 +vn 0.414401 -0.296294 -0.860513 +vn 0.623478 -0.006117 -0.781817 +vn 0.595493 -0.296294 -0.746725 +vn 0.781817 -0.006117 -0.623478 +vn 0.746725 -0.296294 -0.595493 +vn 0.900952 -0.006117 -0.433876 +vn 0.860512 -0.296294 -0.414401 +vn 0.974910 -0.006117 -0.222517 +vn 0.931150 -0.296294 -0.212530 +vn 0.999981 -0.006117 0.000000 +vn 0.955097 -0.296294 0.000000 +vn 0.974910 -0.006117 0.222517 +vn 0.931150 -0.296294 0.212529 +vn 0.900952 -0.006117 0.433876 +vn 0.860513 -0.296294 0.414400 +vn 0.781817 -0.006117 0.623478 +vn 0.746725 -0.296294 0.595493 +vn 0.623478 -0.006117 0.781817 +vn 0.595493 -0.296294 0.746725 +vn 0.433875 -0.006117 0.900952 +vn 0.414401 -0.296294 0.860513 +vn 0.222517 -0.006117 0.974910 +vn 0.212529 -0.296294 0.931150 +vn 0.354916 -0.575221 0.736991 +vn 0.510014 -0.575221 0.639537 +vn 0.182022 -0.575221 0.797489 +vn 0.182022 -0.575221 -0.797489 +vn 0.354916 -0.575221 -0.736991 +vn 0.510013 -0.575221 -0.639537 +vn 0.639537 -0.575221 -0.510013 +vn 0.736991 -0.575221 -0.354916 +vn 0.797490 -0.575220 -0.182022 +vn 0.817999 -0.575220 0.000000 +vn 0.797489 -0.575221 0.182022 +vn 0.736991 -0.575221 0.354916 +vn 0.639537 -0.575221 0.510013 +vn 0.201902 -0.946117 0.253177 +vn 0.253177 -0.946117 0.201902 +vn 0.463668 -0.805163 0.369762 +vn 0.369763 -0.805163 0.463668 +vn 0.140502 -0.946117 0.291757 +vn 0.257316 -0.805164 0.534322 +vn 0.072058 -0.946117 0.315707 +vn 0.131967 -0.805163 0.578184 +vn 0.072058 -0.946117 -0.315707 +vn 0.131967 -0.805164 -0.578184 +vn 0.140503 -0.946117 -0.291757 +vn 0.257316 -0.805164 -0.534322 +vn 0.201902 -0.946117 -0.253178 +vn 0.369762 -0.805164 -0.463667 +vn 0.253177 -0.946117 -0.201902 +vn 0.463667 -0.805164 -0.369762 +vn 0.291757 -0.946117 -0.140503 +vn 0.534322 -0.805164 -0.257316 +vn 0.315706 -0.946117 -0.072058 +vn 0.578184 -0.805163 -0.131967 +vn 0.323827 -0.946116 0.000000 +vn 0.593054 -0.805163 0.000000 +vn 0.315710 -0.946116 0.072059 +vn 0.578184 -0.805163 0.131967 +vn 0.291758 -0.946116 0.140504 +vn 0.534323 -0.805163 0.257316 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.998395 -0.056639 +vn -0.012603 0.998395 -0.055220 +vn -0.024575 0.998395 -0.051030 +vn -0.035314 0.998395 -0.044283 +vn -0.044282 0.998395 -0.035314 +vn -0.051030 0.998395 -0.024576 +vn -0.055219 0.998395 -0.012605 +vn -0.056639 0.998395 0.000000 +vn -0.055219 0.998395 0.012604 +vn -0.051030 0.998395 0.024576 +vn -0.044282 0.998395 0.035314 +vn -0.035314 0.998395 0.044281 +vn -0.024575 0.998395 0.051030 +vn -0.012603 0.998395 0.055219 +vn 0.000000 0.998395 0.056639 +vn -0.222494 0.015512 0.974811 +vn 0.000000 0.015512 0.999880 +vn -0.222494 0.015512 -0.974811 +vn 0.000000 0.015512 -0.999880 +vn -0.433832 0.015512 -0.900860 +vn -0.623415 0.015512 -0.781738 +vn -0.781737 0.015512 -0.623415 +vn -0.900860 0.015512 -0.433832 +vn -0.974811 0.015512 -0.222494 +vn -0.999880 0.015512 0.000000 +vn -0.974811 0.015512 0.222494 +vn -0.900861 0.015512 0.433831 +vn -0.781738 0.015512 0.623414 +vn -0.623415 0.015512 0.781737 +vn -0.433831 0.015512 0.900861 +vn 0.222494 0.015512 -0.974811 +vn 0.433832 0.015512 -0.900860 +vn 0.623415 0.015512 -0.781738 +vn 0.781737 0.015512 -0.623415 +vn 0.900860 0.015512 -0.433832 +vn 0.974811 0.015512 -0.222494 +vn 0.999880 0.015512 0.000000 +vn 0.974811 0.015512 0.222494 +vn 0.900861 0.015512 0.433831 +vn 0.781738 0.015512 0.623415 +vn 0.623415 0.015512 0.781737 +vn 0.433831 0.015512 0.900861 +vn 0.222494 0.015512 0.974811 +vn 0.012603 0.998395 0.055219 +vn 0.024575 0.998395 0.051030 +vn 0.035314 0.998395 0.044282 +vn 0.044282 0.998395 0.035314 +vn 0.051030 0.998395 0.024575 +vn 0.055219 0.998395 0.012604 +vn 0.056639 0.998395 0.000000 +vn 0.055219 0.998395 -0.012605 +vn 0.051030 0.998395 -0.024576 +vn 0.044282 0.998395 -0.035314 +vn 0.035314 0.998395 -0.044282 +vn 0.024575 0.998395 -0.051030 +vn 0.012603 0.998395 -0.055219 +vn 0.000000 0.169480 -0.985534 +vn -0.219302 0.169481 -0.960824 +vn -0.211496 0.310860 -0.926626 +vn -0.000001 0.310860 -0.950456 +vn -0.427607 0.169481 -0.887935 +vn -0.412387 0.310860 -0.856331 +vn -0.614470 0.169480 -0.770521 +vn -0.592599 0.310860 -0.743096 +vn -0.770521 0.169480 -0.614471 +vn -0.743096 0.310860 -0.592600 +vn -0.887935 0.169480 -0.427607 +vn -0.856331 0.310860 -0.412387 +vn -0.960824 0.169480 -0.219302 +vn -0.926626 0.310859 -0.211496 +vn -0.985533 0.169481 0.000000 +vn -0.950456 0.310859 0.000000 +vn -0.960824 0.169481 0.219302 +vn -0.926626 0.310860 0.211497 +vn -0.887935 0.169481 0.427607 +vn -0.856331 0.310860 0.412387 +vn -0.770521 0.169480 0.614470 +vn -0.743096 0.310860 0.592599 +vn -0.614471 0.169480 0.770521 +vn -0.592599 0.310860 0.743096 +vn -0.427607 0.169480 0.887935 +vn -0.412387 0.310860 0.856331 +vn -0.219302 0.169480 0.960824 +vn -0.211496 0.310860 0.926626 +vn 0.000000 0.169480 0.985534 +vn 0.000000 0.310860 0.950456 +vn -0.198254 0.454112 -0.868607 +vn 0.000000 0.454112 -0.890945 +vn -0.386567 0.454112 -0.802713 +vn -0.555495 0.454112 -0.696568 +vn -0.696569 0.454112 -0.555495 +vn -0.802713 0.454112 -0.386567 +vn -0.868607 0.454112 -0.198254 +vn -0.890945 0.454112 0.000000 +vn -0.868607 0.454112 0.198254 +vn -0.802713 0.454112 0.386567 +vn -0.696569 0.454112 0.555495 +vn -0.555495 0.454112 0.696569 +vn -0.386566 0.454112 0.802713 +vn -0.198254 0.454112 0.868607 +vn 0.000000 0.454112 0.890945 +vn -0.180644 0.583925 -0.791454 +vn 0.000000 0.583925 -0.811808 +vn -0.352230 0.583925 -0.731413 +vn -0.506154 0.583925 -0.634697 +vn -0.634697 0.583925 -0.506154 +vn -0.731414 0.583925 -0.352230 +vn -0.791454 0.583925 -0.180644 +vn -0.811808 0.583925 0.000000 +vn -0.791454 0.583925 0.180644 +vn -0.731413 0.583925 0.352231 +vn -0.634697 0.583925 0.506153 +vn -0.506154 0.583925 0.634696 +vn -0.352230 0.583925 0.731414 +vn -0.180644 0.583925 0.791454 +vn 0.000000 0.583925 0.811808 +vn -0.159601 0.696823 -0.699260 +vn 0.000000 0.696823 -0.717243 +vn -0.311200 0.696823 -0.646213 +vn -0.447193 0.696823 -0.560763 +vn -0.560762 0.696824 -0.447194 +vn -0.646213 0.696824 -0.311200 +vn -0.699260 0.696824 -0.159601 +vn -0.717243 0.696823 -0.000001 +vn -0.699260 0.696823 0.159601 +vn -0.646213 0.696823 0.311201 +vn -0.560763 0.696824 0.447193 +vn -0.447194 0.696824 0.560762 +vn -0.311200 0.696824 0.646213 +vn -0.159601 0.696823 0.699260 +vn 0.000000 0.696824 0.717243 +vn -0.136308 0.790421 -0.597205 +vn 0.000000 0.790421 -0.612564 +vn -0.265781 0.790422 -0.551901 +vn -0.381927 0.790422 -0.478921 +vn -0.478921 0.790422 -0.381927 +vn -0.551900 0.790422 -0.265781 +vn -0.597205 0.790422 -0.136308 +vn -0.612563 0.790422 0.000000 +vn -0.597205 0.790422 0.136308 +vn -0.551900 0.790421 0.265782 +vn -0.478921 0.790421 0.381927 +vn -0.381927 0.790422 0.478921 +vn -0.265781 0.790422 0.551900 +vn -0.136308 0.790421 0.597206 +vn 0.000000 0.790422 0.612563 +vn -0.112059 0.863943 -0.490964 +vn 0.000000 0.863942 -0.503591 +vn -0.218499 0.863943 -0.453718 +vn -0.313983 0.863943 -0.393722 +vn -0.393723 0.863943 -0.313983 +vn -0.453719 0.863943 -0.218500 +vn -0.490964 0.863943 -0.112059 +vn -0.503590 0.863943 0.000001 +vn -0.490964 0.863943 0.112060 +vn -0.453719 0.863943 0.218500 +vn -0.393722 0.863943 0.313983 +vn -0.313983 0.863943 0.393723 +vn -0.218500 0.863943 0.453719 +vn -0.112059 0.863943 0.490964 +vn 0.000000 0.863943 0.503590 +vn -0.088047 0.918388 -0.385761 +vn 0.000000 0.918388 -0.395681 +vn -0.171679 0.918388 -0.356496 +vn -0.246703 0.918388 -0.309356 +vn -0.309356 0.918388 -0.246703 +vn -0.356496 0.918388 -0.171680 +vn -0.356496 0.918388 0.171679 +vn -0.309355 0.918388 0.246704 +vn -0.246703 0.918388 0.309356 +vn -0.171679 0.918388 0.356496 +vn -0.088047 0.918388 0.385760 +vn 0.000000 0.918388 0.395680 +vn -0.065167 0.956156 -0.285516 +vn 0.000000 0.956156 -0.292859 +vn -0.127067 0.956156 -0.263856 +vn -0.182594 0.956156 -0.228967 +vn -0.228966 0.956155 -0.182596 +vn -0.263857 0.956156 -0.127067 +vn -0.263857 0.956156 0.127065 +vn -0.228966 0.956156 0.182595 +vn -0.182594 0.956156 0.228967 +vn -0.127067 0.956156 0.263857 +vn -0.065167 0.956156 0.285516 +vn 0.000000 0.956156 0.292859 +vn -0.043999 0.980256 -0.192773 +vn 0.000000 0.980256 -0.197731 +vn -0.085793 0.980256 -0.178150 +vn -0.123284 0.980256 -0.154593 +vn -0.154593 0.980256 -0.123284 +vn -0.178150 0.980256 -0.085792 +vn -0.192774 0.980256 -0.044000 +vn -0.197732 0.980256 0.000000 +vn -0.192774 0.980256 0.044000 +vn -0.178150 0.980256 0.085793 +vn -0.154593 0.980256 0.123284 +vn -0.123284 0.980256 0.154595 +vn -0.085792 0.980256 0.178152 +vn -0.043999 0.980256 0.192775 +vn 0.000000 0.980256 0.197732 +vn -0.025567 0.993377 -0.112018 +vn 0.000000 0.993377 -0.114899 +vn -0.049853 0.993377 -0.103521 +vn -0.071638 0.993377 -0.089830 +vn -0.089831 0.993378 -0.071635 +vn -0.103520 0.993377 -0.049855 +vn -0.112018 0.993377 -0.025569 +vn -0.114899 0.993377 0.000000 +vn -0.112018 0.993377 0.025570 +vn -0.103520 0.993377 0.049855 +vn -0.089831 0.993377 0.071638 +vn -0.071638 0.993377 0.089834 +vn -0.049853 0.993377 0.103522 +vn -0.025567 0.993377 0.112019 +vn 0.000000 0.993377 0.114900 +vn 0.000000 1.000000 -0.000001 +vn 0.211496 0.310861 -0.926626 +vn 0.219302 0.169480 -0.960824 +vn 0.412388 0.310861 -0.856331 +vn 0.427607 0.169480 -0.887935 +vn 0.592599 0.310860 -0.743096 +vn 0.614470 0.169480 -0.770521 +vn 0.743096 0.310860 -0.592600 +vn 0.770521 0.169480 -0.614471 +vn 0.856331 0.310860 -0.412387 +vn 0.887935 0.169481 -0.427607 +vn 0.926626 0.310860 -0.211497 +vn 0.960824 0.169480 -0.219302 +vn 0.950456 0.310860 -0.000001 +vn 0.985534 0.169480 0.000000 +vn 0.926625 0.310861 0.211497 +vn 0.960824 0.169481 0.219302 +vn 0.856331 0.310860 0.412388 +vn 0.887935 0.169481 0.427607 +vn 0.743096 0.310860 0.592599 +vn 0.770521 0.169480 0.614470 +vn 0.592600 0.310860 0.743096 +vn 0.614471 0.169480 0.770521 +vn 0.412388 0.310860 0.856331 +vn 0.427607 0.169481 0.887935 +vn 0.211497 0.310860 0.926626 +vn 0.219302 0.169480 0.960824 +vn 0.198254 0.454112 -0.868607 +vn 0.386567 0.454112 -0.802713 +vn 0.555495 0.454112 -0.696569 +vn 0.696568 0.454111 -0.555496 +vn 0.802713 0.454112 -0.386567 +vn 0.868607 0.454112 -0.198254 +vn 0.890945 0.454112 0.000000 +vn 0.868607 0.454112 0.198255 +vn 0.802713 0.454112 0.386567 +vn 0.696569 0.454112 0.555495 +vn 0.555495 0.454112 0.696569 +vn 0.386566 0.454112 0.802714 +vn 0.198254 0.454112 0.868607 +vn 0.180644 0.583925 -0.791454 +vn 0.352230 0.583925 -0.731414 +vn 0.506154 0.583925 -0.634697 +vn 0.634697 0.583924 -0.506154 +vn 0.731413 0.583925 -0.352231 +vn 0.791454 0.583925 -0.180644 +vn 0.811808 0.583925 0.000000 +vn 0.791454 0.583925 0.180645 +vn 0.731414 0.583925 0.352230 +vn 0.634697 0.583925 0.506154 +vn 0.506154 0.583925 0.634697 +vn 0.352230 0.583925 0.731414 +vn 0.180644 0.583925 0.791454 +vn 0.159601 0.696824 -0.699260 +vn 0.311200 0.696824 -0.646213 +vn 0.447194 0.696824 -0.560762 +vn 0.560763 0.696824 -0.447193 +vn 0.646213 0.696824 -0.311200 +vn 0.699260 0.696824 -0.159601 +vn 0.717242 0.696824 0.000000 +vn 0.699260 0.696824 0.159602 +vn 0.646213 0.696824 0.311199 +vn 0.560763 0.696824 0.447193 +vn 0.447193 0.696824 0.560762 +vn 0.311200 0.696824 0.646213 +vn 0.159601 0.696823 0.699260 +vn 0.136308 0.790421 -0.597206 +vn 0.265781 0.790422 -0.551900 +vn 0.381927 0.790422 -0.478921 +vn 0.478921 0.790422 -0.381927 +vn 0.551900 0.790422 -0.265781 +vn 0.597205 0.790422 -0.136308 +vn 0.612563 0.790422 0.000000 +vn 0.597205 0.790422 0.136308 +vn 0.551900 0.790422 0.265781 +vn 0.478921 0.790422 0.381926 +vn 0.381927 0.790422 0.478921 +vn 0.265781 0.790421 0.551901 +vn 0.136308 0.790422 0.597205 +vn 0.112059 0.863942 -0.490965 +vn 0.218500 0.863943 -0.453719 +vn 0.313983 0.863943 -0.393722 +vn 0.393723 0.863943 -0.313983 +vn 0.453719 0.863943 -0.218500 +vn 0.490964 0.863943 -0.112060 +vn 0.503590 0.863943 0.000000 +vn 0.490964 0.863943 0.112060 +vn 0.453719 0.863943 0.218500 +vn 0.393723 0.863943 0.313984 +vn 0.313983 0.863943 0.393723 +vn 0.218499 0.863943 0.453719 +vn 0.112059 0.863943 0.490964 +vn 0.088047 0.918388 -0.385761 +vn 0.171679 0.918388 -0.356497 +vn 0.246703 0.918388 -0.309355 +vn 0.309355 0.918388 -0.246703 +vn 0.356496 0.918388 -0.171680 +vn 0.356496 0.918388 0.171679 +vn 0.309356 0.918388 0.246703 +vn 0.246703 0.918388 0.309356 +vn 0.171679 0.918388 0.356496 +vn 0.088047 0.918388 0.385760 +vn 0.065167 0.956156 -0.285517 +vn 0.127067 0.956156 -0.263857 +vn 0.182594 0.956156 -0.228966 +vn 0.228966 0.956156 -0.182595 +vn 0.263857 0.956156 -0.127067 +vn 0.263857 0.956156 0.127066 +vn 0.228966 0.956156 0.182594 +vn 0.182595 0.956156 0.228965 +vn 0.127067 0.956156 0.263854 +vn 0.065167 0.956156 0.285515 +vn 0.043999 0.980256 -0.192774 +vn 0.085793 0.980256 -0.178151 +vn 0.123284 0.980256 -0.154592 +vn 0.154593 0.980256 -0.123283 +vn 0.178150 0.980256 -0.085793 +vn 0.192774 0.980256 -0.043999 +vn 0.197732 0.980256 -0.000001 +vn 0.192774 0.980256 0.043999 +vn 0.178150 0.980256 0.085793 +vn 0.154593 0.980256 0.123284 +vn 0.123284 0.980256 0.154593 +vn 0.085793 0.980256 0.178149 +vn 0.043999 0.980256 0.192773 +vn 0.025567 0.993377 -0.112018 +vn 0.049853 0.993377 -0.103521 +vn 0.071638 0.993377 -0.089830 +vn 0.089831 0.993377 -0.071637 +vn 0.103520 0.993377 -0.049855 +vn 0.112018 0.993377 -0.025566 +vn 0.114899 0.993377 0.000000 +vn 0.112018 0.993377 0.025569 +vn 0.103520 0.993377 0.049857 +vn 0.089831 0.993377 0.071640 +vn 0.071638 0.993377 0.089833 +vn 0.049853 0.993377 0.103520 +vn 0.025567 0.993377 0.112017 +vn -0.219660 -0.159819 -0.962397 +vn -0.000001 -0.159820 -0.987146 +vn 0.000000 -0.998264 -0.058901 +vn -0.013107 -0.998264 -0.057424 +vn -0.428306 -0.159820 -0.889388 +vn -0.615476 -0.159821 -0.771782 +vn -0.771782 -0.159821 -0.615476 +vn -0.889388 -0.159820 -0.428307 +vn -0.962396 -0.159820 -0.219661 +vn -0.987146 -0.159819 0.000000 +vn -0.962397 -0.159819 0.219660 +vn -0.889388 -0.159820 0.428306 +vn -0.771782 -0.159820 0.615475 +vn -0.615476 -0.159820 0.771781 +vn -0.428307 -0.159820 0.889388 +vn -0.219661 -0.159821 0.962396 +vn 0.000001 -0.159820 0.987146 +vn 0.000000 -1.000000 0.000000 +vn -0.025556 -0.998264 -0.053067 +vn -0.036724 -0.998264 -0.046051 +vn -0.046050 -0.998264 -0.036724 +vn -0.053067 -0.998264 -0.025555 +vn -0.057424 -0.998264 -0.013107 +vn -0.058900 -0.998264 0.000000 +vn -0.057424 -0.998264 0.013107 +vn -0.053067 -0.998264 0.025556 +vn -0.046050 -0.998264 0.036725 +vn -0.036724 -0.998264 0.046051 +vn -0.025556 -0.998264 0.053067 +vn -0.013107 -0.998264 0.057424 +vn 0.000000 -0.998264 0.058900 +vn 0.219661 -0.159822 -0.962396 +vn 0.428307 -0.159821 -0.889388 +vn 0.615476 -0.159821 -0.771782 +vn 0.771782 -0.159821 -0.615475 +vn 0.889388 -0.159821 -0.428306 +vn 0.962396 -0.159820 -0.219661 +vn 0.987146 -0.159820 0.000000 +vn 0.962396 -0.159820 0.219661 +vn 0.889387 -0.159821 0.428308 +vn 0.771782 -0.159821 0.615475 +vn 0.615476 -0.159820 0.771781 +vn 0.428306 -0.159820 0.889388 +vn 0.219660 -0.159820 0.962397 +vn 0.013107 -0.998264 0.057424 +vn 0.013107 -0.998264 -0.057424 +vn 0.025556 -0.998264 -0.053067 +vn 0.036724 -0.998264 -0.046050 +vn 0.046050 -0.998264 -0.036724 +vn 0.053067 -0.998264 -0.025556 +vn 0.057424 -0.998264 -0.013107 +vn 0.058900 -0.998264 0.000000 +vn 0.057424 -0.998264 0.013106 +vn 0.053067 -0.998264 0.025556 +vn 0.046050 -0.998264 0.036724 +vn 0.036724 -0.998264 0.046050 +vn 0.025556 -0.998264 0.053067 +vn -0.036912 -0.999319 -0.000002 +vn -0.034527 -0.999373 0.007881 +vn -0.035182 -0.999237 0.016944 +vn -0.024746 -0.999499 0.019734 +vn -0.019694 -0.999501 0.024697 +vn -0.010578 -0.999703 0.021965 +vn -0.004293 -0.999814 0.018813 +vn 0.000000 -0.999814 0.019289 +vn 0.004293 -0.999814 0.018813 +vn 0.010578 -0.999703 0.021965 +vn 0.019694 -0.999501 0.024697 +vn 0.024746 -0.999499 0.019734 +vn 0.035182 -0.999237 0.016944 +vn 0.034527 -0.999373 0.007881 +vn 0.036912 -0.999319 -0.000001 +vn 0.034527 -0.999373 -0.007881 +vn 0.035182 -0.999237 -0.016943 +vn 0.024746 -0.999499 -0.019735 +vn 0.019695 -0.999501 -0.024697 +vn 0.010578 -0.999703 -0.021965 +vn 0.004294 -0.999814 -0.018813 +vn 0.000000 -0.999814 -0.019289 +vn -0.004294 -0.999814 -0.018813 +vn -0.010578 -0.999703 -0.021965 +vn -0.019695 -0.999501 -0.024697 +vn -0.024746 -0.999499 -0.019735 +vn -0.035182 -0.999237 -0.016943 +vn -0.034527 -0.999373 -0.007881 +vn -0.000001 -1.000000 -0.000002 +vn 0.000001 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000002 -1.000000 0.000000 +vn 0.000003 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000001 +vn 0.000001 -1.000000 -0.000002 +vn -0.000001 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.000002 -1.000000 0.000000 +vn -0.000003 -1.000000 0.000000 +vn -0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn 0.000001 -1.000000 -0.000001 +vn 0.000001 -1.000000 0.000000 +vn -0.020734 0.046316 -0.998712 +vn -0.614803 0.388649 -0.686272 +vn 0.604569 -0.314879 -0.731674 +vn 0.879862 -0.475229 0.000000 +vn 0.604569 -0.314878 0.731675 +vn -0.020736 0.046317 0.998712 +vn -0.614803 0.388649 0.686272 +vn -0.851492 0.524367 0.000000 +vn 0.499517 -0.043951 0.865189 +vn 0.865193 -0.043951 0.499509 +vn 0.000000 -0.043951 0.999034 +vn -0.499517 -0.043951 0.865189 +vn -0.865193 -0.043950 0.499509 +vn -0.999034 -0.043950 0.000000 +vn 0.999034 -0.043950 0.000000 +vn -0.865189 -0.043950 -0.499517 +vn -0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn 0.499517 -0.043951 -0.865189 +vn 0.865189 -0.043950 -0.499517 +vn -0.499985 0.007713 -0.866000 +vn -0.499985 -0.007713 -0.866000 +vn -0.866000 -0.007713 -0.499985 +vn -0.866000 0.007713 -0.499985 +vn 0.000000 0.007713 -0.999970 +vn 0.000000 -0.007713 -0.999970 +vn 0.499985 0.007713 -0.866000 +vn 0.499985 -0.007713 -0.866000 +vn 0.866000 0.007713 -0.499985 +vn 0.866000 -0.007713 -0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 0.007713 0.499985 +vn 0.866000 -0.007713 0.499985 +vn 0.499985 0.007713 0.866000 +vn 0.499985 -0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn 0.000000 -0.007713 0.999970 +vn -0.499985 0.007713 0.866000 +vn -0.499985 -0.007713 0.866000 +vn -0.866000 0.007713 0.499985 +vn -0.866000 -0.007713 0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.999970 -0.007713 0.000000 +vn 0.000000 -0.013768 0.999905 +vn 0.499953 -0.013768 0.865943 +vn -0.499953 -0.013768 0.865943 +vn -0.865948 -0.013768 0.499945 +vn -0.999905 -0.013768 0.000000 +vn -0.865943 -0.013768 -0.499953 +vn -0.499953 -0.013768 -0.865943 +vn 0.000000 -0.013768 -0.999905 +vn 0.499953 -0.013768 -0.865943 +vn 0.865943 -0.013768 -0.499953 +vn 0.999905 -0.013768 0.000000 +vn 0.865948 -0.013768 0.499945 +vn -0.001996 0.008585 -0.999961 +vn 0.609220 0.361371 -0.705876 +vn 0.614802 0.388648 -0.686273 +vn 0.020735 0.046316 -0.998712 +vn -0.615214 -0.345341 -0.708697 +vn -0.604568 -0.314878 -0.731675 +vn -0.870284 -0.492551 0.000000 +vn -0.879863 -0.475228 0.000000 +vn -0.615213 -0.345341 0.708697 +vn -0.604568 -0.314878 0.731676 +vn 0.020736 0.046317 0.998712 +vn -0.001995 0.008586 0.999961 +vn 0.609221 0.361371 0.705876 +vn 0.614803 0.388649 0.686272 +vn 0.861840 0.507181 0.000000 +vn 0.851492 0.524367 0.000000 +vn -0.865193 -0.043950 0.499509 +vn -0.499517 -0.043951 0.865189 +vn 0.000000 -0.043951 0.999034 +vn 0.499517 -0.043951 0.865189 +vn 0.865193 -0.043950 0.499509 +vn 0.999034 -0.043950 0.000000 +vn -0.999034 -0.043950 0.000000 +vn 0.865189 -0.043950 -0.499517 +vn 0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn -0.499517 -0.043951 -0.865189 +vn -0.865189 -0.043950 -0.499517 +vn 0.000000 -0.013768 0.999905 +vn -0.499953 -0.013768 0.865943 +vn 0.499953 -0.013768 0.865943 +vn 0.865948 -0.013768 0.499945 +vn 0.999905 -0.013768 0.000000 +vn 0.865943 -0.013768 -0.499953 +vn 0.499953 -0.013768 -0.865943 +vn 0.000000 -0.013768 -0.999905 +vn -0.499953 -0.013768 -0.865943 +vn -0.865943 -0.013768 -0.499953 +vn -0.999905 -0.013768 0.000000 +vn -0.865948 -0.013768 0.499945 +vn -0.768903 0.058373 -0.636696 +vn -0.217514 0.376642 -0.900460 +vn 0.333953 0.694994 -0.636757 +vn 0.562362 0.826891 0.000002 +vn 0.333954 0.694992 0.636759 +vn -0.217515 0.376641 0.900460 +vn -0.768903 0.058373 0.636695 +vn -0.997294 -0.073511 -0.000000 +vn -0.775054 0.444809 -0.448817 +vn -0.386276 0.669201 -0.634792 +vn 0.002361 0.893545 -0.448967 +vn 0.163221 0.986590 -0.000000 +vn 0.002361 0.893546 0.448966 +vn -0.386276 0.669201 0.634792 +vn -0.775053 0.444810 0.448817 +vn -0.936026 0.351932 -0.000000 +vn -0.500086 0.865976 0.000000 +vn 0.499985 0.007713 -0.866000 +vn 0.866000 0.007713 -0.499985 +vn 0.866000 -0.007713 -0.499985 +vn 0.499985 -0.007713 -0.866000 +vn 0.000000 0.007713 -0.999970 +vn 0.000000 -0.007713 -0.999970 +vn -0.499985 0.007713 -0.866000 +vn -0.499985 -0.007713 -0.866000 +vn -0.866000 0.007713 -0.499985 +vn -0.866000 -0.007713 -0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 0.007713 0.499985 +vn -0.866000 -0.007713 0.499985 +vn -0.499985 0.007713 0.866000 +vn -0.499985 -0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn 0.000000 -0.007713 0.999970 +vn 0.499985 0.007713 0.866000 +vn 0.499985 -0.007713 0.866000 +vn 0.866000 0.007713 0.499985 +vn 0.866000 -0.007713 0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.999970 -0.007713 0.000000 +vn -0.289759 -0.942366 -0.167292 +vn -0.468078 -0.841351 -0.270244 +vn -0.270243 -0.841351 -0.468077 +vn -0.167291 -0.942366 -0.289760 +vn 0.000000 -0.841351 -0.540489 +vn 0.000000 -0.942365 -0.334586 +vn 0.270246 -0.841352 -0.468075 +vn 0.167296 -0.942365 -0.289759 +vn 0.468075 -0.841353 -0.270244 +vn 0.289761 -0.942365 -0.167293 +vn 0.540486 -0.841353 0.000000 +vn 0.334585 -0.942366 0.000000 +vn 0.468075 -0.841353 0.270244 +vn 0.289760 -0.942365 0.167294 +vn 0.270246 -0.841352 0.468075 +vn 0.167296 -0.942365 0.289759 +vn 0.000000 -0.841351 0.540489 +vn 0.000000 -0.942365 0.334587 +vn -0.270243 -0.841351 0.468078 +vn -0.167291 -0.942365 0.289760 +vn -0.468077 -0.841351 0.270245 +vn -0.289759 -0.942366 0.167293 +vn -0.540491 -0.841350 -0.000000 +vn -0.334583 -0.942366 0.000000 +vn -0.631864 -0.683860 -0.364805 +vn -0.364803 -0.683861 -0.631865 +vn 0.000000 -0.683861 -0.729612 +vn 0.364808 -0.683861 -0.631862 +vn 0.631860 -0.683863 -0.364806 +vn 0.729609 -0.683865 0.000000 +vn 0.631860 -0.683864 0.364806 +vn 0.364807 -0.683863 0.631860 +vn 0.000000 -0.683864 0.729610 +vn -0.364802 -0.683863 0.631863 +vn -0.631864 -0.683861 0.364804 +vn -0.729613 -0.683860 -0.000000 +vn -0.758869 -0.481825 -0.438135 +vn -0.438134 -0.481821 -0.758872 +vn -0.000000 -0.481821 -0.876270 +vn 0.438134 -0.481822 -0.758872 +vn 0.758870 -0.481823 -0.438136 +vn 0.876268 -0.481824 0.000000 +vn 0.758870 -0.481823 0.438136 +vn 0.438134 -0.481822 0.758872 +vn -0.000000 -0.481822 0.876269 +vn -0.438134 -0.481821 0.758872 +vn -0.758870 -0.481825 0.438135 +vn -0.876266 -0.481828 -0.000000 +vn -0.838785 -0.248836 -0.484273 +vn -0.484270 -0.248841 -0.838785 +vn 0.000002 -0.248841 -0.968544 +vn 0.484273 -0.248839 -0.838784 +vn 0.838784 -0.248838 -0.484274 +vn 0.968546 -0.248836 0.000000 +vn 0.838785 -0.248844 0.484270 +vn 0.484275 -0.248853 0.838779 +vn 0.000002 -0.248857 0.968540 +vn -0.484271 -0.248855 0.838781 +vn -0.838786 -0.248843 0.484269 +vn -0.968547 -0.248833 0.000000 +vn 0.000001 -1.000000 0.000000 +vn -0.289764 -0.942364 -0.167296 +vn -0.468092 -0.841342 -0.270247 +vn -0.270246 -0.841346 -0.468085 +vn -0.167290 -0.942363 -0.289770 +vn 0.000000 -0.841347 -0.540496 +vn -0.000000 -0.942361 -0.334597 +vn 0.270251 -0.841346 -0.468082 +vn 0.167306 -0.942360 -0.289769 +vn 0.468085 -0.841348 -0.270242 +vn 0.289775 -0.942361 -0.167291 +vn 0.540496 -0.841347 -0.000000 +vn 0.334596 -0.942362 -0.000000 +vn 0.468085 -0.841348 0.270243 +vn 0.289774 -0.942361 0.167291 +vn 0.270251 -0.841346 0.468083 +vn 0.167306 -0.942360 0.289769 +vn 0.000000 -0.841347 0.540496 +vn -0.000000 -0.942361 0.334597 +vn -0.270246 -0.841346 0.468085 +vn -0.167291 -0.942363 0.289770 +vn -0.468092 -0.841342 0.270247 +vn -0.289764 -0.942364 0.167296 +vn -0.540505 -0.841341 -0.000000 +vn -0.334592 -0.942363 -0.000000 +vn -0.631879 -0.683841 -0.364817 +vn -0.364817 -0.683841 -0.631878 +vn 0.000000 -0.683847 -0.729626 +vn 0.364815 -0.683845 -0.631875 +vn 0.631876 -0.683847 -0.364810 +vn 0.729620 -0.683852 0.000000 +vn 0.631876 -0.683847 0.364810 +vn 0.364815 -0.683845 0.631875 +vn 0.000000 -0.683847 0.729626 +vn -0.364817 -0.683841 0.631878 +vn -0.631879 -0.683841 0.364816 +vn -0.729628 -0.683844 -0.000000 +vn -0.758871 -0.481814 -0.438144 +vn -0.438132 -0.481807 -0.758883 +vn 0.000000 -0.481808 -0.876277 +vn 0.438141 -0.481808 -0.758876 +vn 0.758876 -0.481807 -0.438144 +vn 0.876278 -0.481806 -0.000000 +vn 0.758876 -0.481807 0.438144 +vn 0.438141 -0.481808 0.758876 +vn 0.000000 -0.481808 0.876277 +vn -0.438132 -0.481807 0.758883 +vn -0.758871 -0.481814 0.438144 +vn -0.876269 -0.481823 0.000000 +vn -0.838782 -0.248835 -0.484278 +vn -0.484267 -0.248839 -0.838788 +vn 0.000000 -0.248833 -0.968546 +vn 0.484281 -0.248830 -0.838782 +vn 0.838790 -0.248820 -0.484273 +vn 0.968552 -0.248812 -0.000000 +vn 0.838790 -0.248820 0.484273 +vn 0.484281 -0.248830 0.838782 +vn -0.000000 -0.248833 0.968547 +vn -0.484267 -0.248839 0.838788 +vn -0.838782 -0.248835 0.484279 +vn -0.968548 -0.248826 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.289764 0.942364 -0.167296 +vn -0.167290 0.942363 -0.289769 +vn -0.270246 0.841346 -0.468086 +vn -0.468092 0.841342 -0.270247 +vn -0.000000 0.942361 -0.334598 +vn 0.000000 0.841346 -0.540497 +vn 0.167306 0.942361 -0.289767 +vn 0.270251 0.841346 -0.468082 +vn 0.289775 0.942361 -0.167291 +vn 0.468085 0.841347 -0.270243 +vn 0.334596 0.942362 -0.000000 +vn 0.540495 0.841347 -0.000000 +vn 0.289774 0.942361 0.167290 +vn 0.468085 0.841347 0.270243 +vn 0.167306 0.942361 0.289767 +vn 0.270251 0.841346 0.468082 +vn 0.000000 0.942361 0.334598 +vn 0.000000 0.841346 0.540496 +vn -0.167291 0.942363 0.289768 +vn -0.270246 0.841346 0.468085 +vn -0.289763 0.942364 0.167296 +vn -0.468091 0.841342 0.270247 +vn -0.334592 0.942363 -0.000000 +vn -0.540505 0.841341 -0.000000 +vn -0.364806 0.683864 -0.631860 +vn -0.631860 0.683864 -0.364805 +vn 0.000000 0.683869 -0.729605 +vn 0.364804 0.683868 -0.631857 +vn 0.631858 0.683869 -0.364800 +vn 0.729600 0.683875 -0.000000 +vn 0.631858 0.683869 0.364800 +vn 0.364804 0.683868 0.631857 +vn 0.000000 0.683869 0.729604 +vn -0.364806 0.683864 0.631859 +vn -0.631860 0.683864 0.364805 +vn -0.729607 0.683867 0.000000 +vn -0.438124 0.481837 -0.758868 +vn -0.758856 0.481845 -0.438135 +vn 0.000000 0.481839 -0.876260 +vn 0.438133 0.481839 -0.758861 +vn 0.758861 0.481837 -0.438136 +vn 0.876261 0.481836 -0.000000 +vn 0.758861 0.481837 0.438135 +vn 0.438133 0.481839 0.758862 +vn -0.000000 0.481839 0.876260 +vn -0.438124 0.481838 0.758868 +vn -0.758856 0.481845 0.438135 +vn -0.876252 0.481854 0.000000 +vn -0.484268 0.248828 -0.838790 +vn -0.838785 0.248824 -0.484280 +vn -0.000000 0.248822 -0.968549 +vn 0.484282 0.248819 -0.838785 +vn 0.838792 0.248809 -0.484275 +vn 0.968554 0.248801 -0.000000 +vn 0.838792 0.248809 0.484275 +vn 0.484282 0.248819 0.838785 +vn -0.000000 0.248821 0.968549 +vn -0.484268 0.248828 0.838790 +vn -0.838784 0.248824 0.484280 +vn -0.968551 0.248815 0.000000 +vn 0.000000 1.000000 0.000001 +vn 0.289759 -0.942366 -0.167292 +vn 0.167291 -0.942365 -0.289760 +vn 0.270243 -0.841351 -0.468078 +vn 0.468078 -0.841351 -0.270244 +vn -0.000000 -0.942365 -0.334586 +vn -0.000000 -0.841351 -0.540489 +vn -0.167296 -0.942365 -0.289759 +vn -0.270246 -0.841352 -0.468075 +vn -0.289761 -0.942365 -0.167293 +vn -0.468075 -0.841353 -0.270244 +vn -0.334585 -0.942366 0.000000 +vn -0.540486 -0.841353 0.000000 +vn -0.289760 -0.942365 0.167294 +vn -0.468075 -0.841353 0.270244 +vn -0.167296 -0.942365 0.289759 +vn -0.270246 -0.841352 0.468075 +vn -0.000000 -0.942365 0.334587 +vn -0.000000 -0.841351 0.540489 +vn 0.167291 -0.942366 0.289760 +vn 0.270243 -0.841351 0.468078 +vn 0.289759 -0.942366 0.167293 +vn 0.468077 -0.841351 0.270245 +vn 0.334583 -0.942366 -0.000000 +vn 0.540491 -0.841350 -0.000000 +vn 0.364803 -0.683861 -0.631865 +vn 0.631864 -0.683861 -0.364805 +vn -0.000000 -0.683861 -0.729612 +vn -0.364808 -0.683861 -0.631862 +vn -0.631860 -0.683863 -0.364806 +vn -0.729609 -0.683865 -0.000000 +vn -0.631860 -0.683864 0.364806 +vn -0.364807 -0.683863 0.631860 +vn -0.000000 -0.683864 0.729609 +vn 0.364802 -0.683863 0.631863 +vn 0.631864 -0.683861 0.364804 +vn 0.729613 -0.683861 -0.000000 +vn 0.438134 -0.481821 -0.758872 +vn 0.758869 -0.481825 -0.438135 +vn 0.000000 -0.481821 -0.876270 +vn -0.438134 -0.481822 -0.758872 +vn -0.758870 -0.481823 -0.438136 +vn -0.876268 -0.481824 0.000000 +vn -0.758870 -0.481823 0.438136 +vn -0.438134 -0.481822 0.758872 +vn 0.000000 -0.481822 0.876269 +vn 0.438134 -0.481821 0.758872 +vn 0.758870 -0.481825 0.438135 +vn 0.876266 -0.481828 -0.000000 +vn 0.484270 -0.248841 -0.838785 +vn 0.838785 -0.248836 -0.484273 +vn -0.000002 -0.248841 -0.968545 +vn -0.484274 -0.248839 -0.838784 +vn -0.838784 -0.248838 -0.484274 +vn -0.968546 -0.248836 0.000000 +vn -0.838785 -0.248844 0.484270 +vn -0.484275 -0.248853 0.838779 +vn -0.000002 -0.248857 0.968540 +vn 0.484271 -0.248855 0.838781 +vn 0.838786 -0.248843 0.484269 +vn 0.968547 -0.248832 0.000000 +vn -0.000001 -1.000000 0.000000 +vn 0.289764 -0.942364 -0.167297 +vn 0.167290 -0.942363 -0.289770 +vn 0.270246 -0.841346 -0.468085 +vn 0.468091 -0.841342 -0.270247 +vn 0.000000 -0.942361 -0.334597 +vn -0.000000 -0.841347 -0.540496 +vn -0.167306 -0.942360 -0.289769 +vn -0.270251 -0.841346 -0.468082 +vn -0.289775 -0.942361 -0.167291 +vn -0.468085 -0.841347 -0.270243 +vn -0.334596 -0.942362 -0.000000 +vn -0.540496 -0.841347 -0.000000 +vn -0.289774 -0.942361 0.167291 +vn -0.468085 -0.841348 0.270243 +vn -0.167306 -0.942360 0.289769 +vn -0.270251 -0.841346 0.468083 +vn -0.000000 -0.942361 0.334598 +vn -0.000000 -0.841347 0.540496 +vn 0.167291 -0.942363 0.289770 +vn 0.270246 -0.841346 0.468085 +vn 0.289763 -0.942364 0.167296 +vn 0.468091 -0.841342 0.270247 +vn 0.334592 -0.942363 -0.000000 +vn 0.540505 -0.841341 -0.000000 +vn 0.364817 -0.683841 -0.631879 +vn 0.631879 -0.683841 -0.364817 +vn -0.000000 -0.683847 -0.729626 +vn -0.364815 -0.683845 -0.631875 +vn -0.631876 -0.683846 -0.364810 +vn -0.729621 -0.683852 0.000000 +vn -0.631876 -0.683846 0.364810 +vn -0.364815 -0.683845 0.631875 +vn -0.000000 -0.683847 0.729626 +vn 0.364817 -0.683841 0.631878 +vn 0.631879 -0.683841 0.364816 +vn 0.729628 -0.683844 -0.000000 +vn 0.438132 -0.481806 -0.758883 +vn 0.758871 -0.481815 -0.438144 +vn 0.000000 -0.481808 -0.876277 +vn -0.438141 -0.481808 -0.758876 +vn -0.758876 -0.481807 -0.438144 +vn -0.876278 -0.481805 0.000000 +vn -0.758876 -0.481807 0.438144 +vn -0.438142 -0.481808 0.758876 +vn 0.000000 -0.481808 0.876277 +vn 0.438132 -0.481807 0.758883 +vn 0.758871 -0.481814 0.438144 +vn 0.876269 -0.481823 -0.000000 +vn 0.484267 -0.248839 -0.838788 +vn 0.838782 -0.248835 -0.484279 +vn -0.000000 -0.248833 -0.968547 +vn -0.484281 -0.248830 -0.838782 +vn -0.838790 -0.248820 -0.484273 +vn -0.968552 -0.248813 0.000000 +vn -0.838790 -0.248820 0.484274 +vn -0.484281 -0.248830 0.838782 +vn 0.000000 -0.248833 0.968547 +vn 0.484267 -0.248839 0.838788 +vn 0.838782 -0.248835 0.484278 +vn 0.968548 -0.248826 -0.000000 +vn -0.000000 -1.000000 -0.000000 +vn 0.289764 0.942364 -0.167296 +vn 0.468092 0.841342 -0.270247 +vn 0.270246 0.841346 -0.468086 +vn 0.167290 0.942363 -0.289769 +vn -0.000000 0.841346 -0.540497 +vn 0.000000 0.942361 -0.334598 +vn -0.270250 0.841346 -0.468083 +vn -0.167306 0.942361 -0.289767 +vn -0.468085 0.841348 -0.270243 +vn -0.289775 0.942361 -0.167290 +vn -0.540496 0.841347 0.000000 +vn -0.334596 0.942362 -0.000000 +vn -0.468085 0.841347 0.270243 +vn -0.289775 0.942361 0.167291 +vn -0.270251 0.841346 0.468083 +vn -0.167306 0.942361 0.289768 +vn -0.000000 0.841346 0.540497 +vn 0.000000 0.942361 0.334598 +vn 0.270246 0.841346 0.468086 +vn 0.167291 0.942363 0.289768 +vn 0.468092 0.841342 0.270247 +vn 0.289764 0.942364 0.167295 +vn 0.540505 0.841341 -0.000000 +vn 0.334592 0.942363 -0.000000 +vn 0.631860 0.683864 -0.364805 +vn 0.364806 0.683864 -0.631860 +vn -0.000000 0.683869 -0.729605 +vn -0.364804 0.683868 -0.631857 +vn -0.631858 0.683869 -0.364800 +vn -0.729599 0.683875 -0.000000 +vn -0.631858 0.683869 0.364799 +vn -0.364804 0.683868 0.631857 +vn -0.000000 0.683869 0.729605 +vn 0.364806 0.683864 0.631860 +vn 0.631860 0.683864 0.364805 +vn 0.729607 0.683867 -0.000000 +vn 0.758856 0.481845 -0.438135 +vn 0.438124 0.481837 -0.758868 +vn -0.000000 0.481839 -0.876260 +vn -0.438133 0.481839 -0.758861 +vn -0.758861 0.481837 -0.438135 +vn -0.876261 0.481836 0.000000 +vn -0.758861 0.481838 0.438135 +vn -0.438133 0.481839 0.758861 +vn 0.000000 0.481839 0.876260 +vn 0.438124 0.481838 0.758868 +vn 0.758857 0.481845 0.438135 +vn 0.876252 0.481853 -0.000000 +vn 0.838785 0.248824 -0.484280 +vn 0.484268 0.248828 -0.838790 +vn -0.000000 0.248822 -0.968549 +vn -0.484282 0.248819 -0.838785 +vn -0.838792 0.248809 -0.484275 +vn -0.968555 0.248801 0.000000 +vn -0.838792 0.248809 0.484275 +vn -0.484282 0.248819 0.838785 +vn -0.000000 0.248822 0.968549 +vn 0.484268 0.248828 0.838790 +vn 0.838785 0.248824 0.484280 +vn 0.968551 0.248815 0.000000 +vn -0.000000 1.000000 0.000000 +s 1 +g Andy_GEO +f 1/1/1 2/2/2 10/10/3 9/9/4 +f 2/2/2 3/3/5 11/11/6 10/10/3 +f 3/3/5 4/4/7 12/927/8 11/11/6 +f 4/758/7 5/5/9 13/13/10 12/12/8 +f 5/5/9 6/6/11 14/14/12 13/13/10 +f 6/6/11 7/7/13 15/15/14 14/14/12 +f 7/7/13 8/8/15 16/16/16 15/15/14 +f 8/8/15 1/1/1 9/9/4 16/16/16 +f 9/9/4 10/10/3 18/18/17 17/17/18 +f 10/10/3 11/11/6 19/19/19 18/18/17 +f 11/11/6 12/927/8 20/928/20 19/19/19 +f 12/12/8 13/13/10 21/21/21 20/20/20 +f 13/13/10 14/14/12 22/22/22 21/21/21 +f 14/14/12 15/15/14 23/23/23 22/22/22 +f 15/15/14 16/16/16 24/24/24 23/23/23 +f 16/16/16 9/9/4 17/17/18 24/24/24 +f 17/17/18 18/18/17 25/25/25 +f 18/18/17 19/19/19 25/26/25 +f 19/19/19 20/928/20 25/27/25 +f 20/20/20 21/21/21 25/28/25 +f 21/21/21 22/22/22 25/29/25 +f 22/22/22 23/23/23 25/30/25 +f 23/23/23 24/24/24 25/31/25 +f 24/24/24 17/17/18 25/32/25 +f 32/33/26 26/34/27 33/35/28 +f 34/36/29 27/37/30 26/34/27 32/33/26 +f 35/38/31 29/39/32 28/40/33 36/41/34 +f 37/42/35 29/39/32 35/38/31 +f 38/43/36 30/44/37 29/39/32 37/42/35 +f 33/35/28 26/34/27 30/44/37 38/43/36 +f 39/45/38 31/46/39 27/37/30 34/36/29 +f 36/41/34 28/40/33 31/46/39 39/45/38 +f 46/47/40 47/48/41 40/49/42 +f 48/50/43 46/47/40 40/49/42 41/51/44 +f 49/52/45 50/53/46 42/54/47 43/55/48 +f 51/56/49 49/52/45 43/55/48 +f 52/57/50 51/56/49 43/55/48 44/58/51 +f 47/48/41 52/57/50 44/58/51 40/49/42 +f 53/59/52 48/50/43 41/51/44 45/60/53 +f 50/53/46 53/59/52 45/60/53 42/54/47 +s 6 +f 55/61/54 54/62/55 88/63/56 89/64/57 +f 56/65/58 55/61/54 89/64/57 90/66/59 +f 57/67/60 56/65/58 90/66/59 91/68/61 +f 58/69/62 57/67/60 91/68/61 92/70/63 +f 59/71/64 58/69/62 92/70/63 93/72/65 +f 60/73/66 59/71/64 93/72/65 94/74/67 +f 61/75/68 60/73/66 94/74/67 95/76/69 +f 62/77/70 61/75/68 95/76/69 96/78/71 +f 63/79/72 62/77/70 96/78/71 97/80/73 +f 64/81/74 63/79/72 97/80/73 98/82/75 +f 65/83/76 64/81/74 98/82/75 84/84/77 +f 66/85/78 65/83/76 84/84/77 85/86/79 +f 67/87/80 66/85/78 85/86/79 86/88/81 +f 68/89/82 67/87/80 86/88/81 87/90/83 +f 85/86/79 84/84/77 581/91/84 582/92/85 +f 86/88/81 85/86/79 582/92/85 583/93/86 +f 87/90/83 86/88/81 583/93/86 584/94/87 +f 89/64/57 88/63/56 598/95/88 599/96/89 +f 90/66/59 89/64/57 599/96/89 600/97/90 +f 91/68/61 90/66/59 600/97/90 601/98/91 +f 92/70/63 91/68/61 601/98/91 602/99/92 +f 93/72/65 92/70/63 602/99/92 603/100/93 +f 94/74/67 93/72/65 603/100/93 604/101/94 +f 95/76/69 94/74/67 604/101/94 577/102/95 +f 96/78/71 95/76/69 577/102/95 578/103/96 +f 97/80/73 96/78/71 578/103/96 579/104/97 +f 98/82/75 97/80/73 579/104/97 580/105/98 +f 84/84/77 98/82/75 580/105/98 581/91/84 +f 100/106/99 99/107/100 608/108/101 609/109/102 +f 101/110/103 100/106/99 609/109/102 610/111/104 +f 102/112/105 101/110/103 610/111/104 611/113/106 +f 103/114/107 102/112/105 611/113/106 612/115/108 +f 105/116/109 104/117/110 626/118/111 627/119/112 +f 106/120/113 105/116/109 627/119/112 628/121/114 +f 107/122/115 106/120/113 628/121/114 629/123/116 +f 108/124/117 107/122/115 629/123/116 630/125/118 +f 109/126/119 108/124/117 630/125/118 631/127/120 +f 110/128/121 109/126/119 631/127/120 632/129/122 +f 111/130/123 110/128/121 632/129/122 605/131/124 +f 112/132/125 111/130/123 605/131/124 606/133/126 +f 113/134/127 112/132/125 606/133/126 607/135/128 +f 608/108/101 99/107/100 113/134/127 607/135/128 +f 114/136/129 143/137/130 88/63/56 54/62/55 +f 115/138/131 144/139/132 143/137/130 114/136/129 +f 116/140/133 145/141/134 144/139/132 115/138/131 +f 117/142/135 146/143/136 145/141/134 116/140/133 +f 118/144/137 147/145/138 146/143/136 117/142/135 +f 119/146/139 148/147/140 147/145/138 118/144/137 +f 120/148/141 149/149/142 148/147/140 119/146/139 +f 121/150/143 150/151/144 149/149/142 120/148/141 +f 122/152/145 151/153/146 150/151/144 121/150/143 +f 123/154/147 152/155/148 151/153/146 122/152/145 +f 124/156/149 140/157/150 152/155/148 123/154/147 +f 125/158/151 141/159/152 140/157/150 124/156/149 +f 126/160/153 142/161/154 141/159/152 125/158/151 +f 68/89/82 87/90/83 142/161/154 126/160/153 +f 141/159/152 586/162/155 587/163/156 140/157/150 +f 142/161/154 585/164/157 586/162/155 141/159/152 +f 87/90/83 584/94/87 585/164/157 142/161/154 +f 143/137/130 597/165/158 598/95/88 88/63/56 +f 144/139/132 596/166/159 597/165/158 143/137/130 +f 145/141/134 595/167/160 596/166/159 144/139/132 +f 146/143/136 594/168/161 595/167/160 145/141/134 +f 147/145/138 593/169/162 594/168/161 146/143/136 +f 148/147/140 592/170/163 593/169/162 147/145/138 +f 149/149/142 591/171/164 592/170/163 148/147/140 +f 150/151/144 590/172/165 591/171/164 149/149/142 +f 151/153/146 589/173/166 590/172/165 150/151/144 +f 152/155/148 588/174/167 589/173/166 151/153/146 +f 140/157/150 587/163/156 588/174/167 152/155/148 +f 615/175/168 616/176/169 153/177/170 154/178/171 +f 614/179/172 615/175/168 154/178/171 155/180/173 +f 613/181/174 614/179/172 155/180/173 156/182/175 +f 103/114/107 612/115/108 613/181/174 156/182/175 +f 625/183/176 626/118/111 104/117/110 157/184/177 +f 624/185/178 625/183/176 157/184/177 158/186/179 +f 623/187/180 624/185/178 158/186/179 159/188/181 +f 622/189/182 623/187/180 159/188/181 160/190/183 +f 621/191/184 622/189/182 160/190/183 161/192/185 +f 620/193/186 621/191/184 161/192/185 162/194/187 +f 619/195/188 620/193/186 162/194/187 163/196/189 +f 618/197/190 619/195/188 163/196/189 164/198/191 +f 617/199/192 618/197/190 164/198/191 165/200/193 +f 153/177/170 616/176/169 617/199/192 165/200/193 +f 166/201/194 196/202/195 194/203/196 +f 166/201/194 194/203/196 192/204/197 +f 166/201/194 192/204/197 190/205/198 +f 166/201/194 190/205/198 188/206/199 +f 166/201/194 188/206/199 186/207/200 +f 166/201/194 186/207/200 184/208/201 +f 166/201/194 184/208/201 182/209/202 +f 166/201/194 182/209/202 180/210/203 +f 166/201/194 180/210/203 178/211/204 +f 166/201/194 178/211/204 176/212/205 +f 166/201/194 176/212/205 174/213/206 +f 166/201/194 174/213/206 172/214/207 +f 166/201/194 172/214/207 170/215/208 +f 166/201/194 170/215/208 167/216/209 +f 167/216/209 170/215/208 169/217/210 168/218/211 +f 54/219/55 55/220/54 193/221/212 195/222/213 +f 193/221/212 55/220/54 56/223/58 191/224/214 +f 56/223/58 57/225/60 189/226/215 191/224/214 +f 57/225/60 58/227/62 187/228/216 189/226/215 +f 58/227/62 59/229/64 185/230/217 187/228/216 +f 59/229/64 60/231/66 183/232/218 185/230/217 +f 60/231/66 61/233/68 181/234/219 183/232/218 +f 61/233/68 62/235/70 179/236/220 181/234/219 +f 62/235/70 63/237/72 177/238/221 179/236/220 +f 63/237/72 64/239/74 175/240/222 177/238/221 +f 64/239/74 65/241/76 173/242/223 175/240/222 +f 65/241/76 66/243/78 171/244/224 173/242/223 +f 66/243/78 67/245/80 169/246/210 171/244/224 +f 67/245/80 68/247/82 168/248/211 169/246/210 +f 114/252/129 54/249/55 195/250/213 221/251/225 +f 115/254/131 114/252/129 221/251/225 219/253/226 +f 116/256/133 115/254/131 219/253/226 217/255/227 +f 117/258/135 116/256/133 217/255/227 215/257/228 +f 118/260/137 117/258/135 215/257/228 213/259/229 +f 119/262/139 118/260/137 213/259/229 211/261/230 +f 120/264/141 119/262/139 211/261/230 209/263/231 +f 121/266/143 120/264/141 209/263/231 207/265/232 +f 122/268/145 121/266/143 207/265/232 205/267/233 +f 123/270/147 122/268/145 205/267/233 203/269/234 +f 124/272/149 123/270/147 203/269/234 201/271/235 +f 125/274/151 124/272/149 201/271/235 199/273/236 +f 126/276/153 125/274/151 199/273/236 197/275/237 +f 68/247/82 126/276/153 197/275/237 168/248/211 +f 166/201/194 167/216/209 198/277/238 +f 166/201/194 198/277/238 200/278/239 +f 166/201/194 200/278/239 202/279/240 +f 166/201/194 202/279/240 204/280/241 +f 166/201/194 204/280/241 206/281/242 +f 166/201/194 206/281/242 208/282/243 +f 166/201/194 208/282/243 210/283/244 +f 166/201/194 210/283/244 212/284/245 +f 166/201/194 212/284/245 214/285/246 +f 166/201/194 214/285/246 216/286/247 +f 166/201/194 216/286/247 218/287/248 +f 166/201/194 218/287/248 220/288/249 +f 166/201/194 220/288/249 222/289/250 +f 166/201/194 222/289/250 196/202/195 +f 170/215/208 172/214/207 171/290/224 169/217/210 +f 172/214/207 174/213/206 173/291/223 171/290/224 +f 174/213/206 176/212/205 175/292/222 173/291/223 +f 176/212/205 178/211/204 177/293/221 175/292/222 +f 178/211/204 180/210/203 179/294/220 177/293/221 +f 180/210/203 182/209/202 181/295/219 179/294/220 +f 182/209/202 184/208/201 183/296/218 181/295/219 +f 184/208/201 186/207/200 185/297/217 183/296/218 +f 186/207/200 188/206/199 187/298/216 185/297/217 +f 188/206/199 190/205/198 189/299/215 187/298/216 +f 190/205/198 192/204/197 191/300/214 189/299/215 +f 192/204/197 194/203/196 193/301/212 191/300/214 +f 194/203/196 196/202/195 195/302/213 193/301/212 +f 195/302/213 196/202/195 222/289/250 221/303/225 +f 198/277/238 167/216/209 168/218/211 197/304/237 +f 200/278/239 198/277/238 197/304/237 199/305/236 +f 202/279/240 200/278/239 199/305/236 201/306/235 +f 204/280/241 202/279/240 201/306/235 203/307/234 +f 206/281/242 204/280/241 203/307/234 205/308/233 +f 208/282/243 206/281/242 205/308/233 207/309/232 +f 210/283/244 208/282/243 207/309/232 209/310/231 +f 212/284/245 210/283/244 209/310/231 211/311/230 +f 214/285/246 212/284/245 211/311/230 213/312/229 +f 216/286/247 214/285/246 213/312/229 215/313/228 +f 218/287/248 216/286/247 215/313/228 217/314/227 +f 220/288/249 218/287/248 217/314/227 219/315/226 +f 222/289/250 220/288/249 219/315/226 221/303/225 +s 1 +f 223/316/251 224/317/252 239/318/253 238/319/254 +f 224/317/252 225/320/255 240/321/256 239/318/253 +f 225/320/255 226/322/257 241/323/258 240/321/256 +f 226/322/257 227/324/259 242/325/260 241/323/258 +f 227/324/259 228/326/261 243/327/262 242/325/260 +f 228/326/261 229/328/263 244/329/264 243/327/262 +f 229/328/263 230/330/265 245/331/266 244/329/264 +f 230/330/265 231/332/267 246/333/268 245/331/266 +f 231/332/267 232/334/269 247/335/270 246/333/268 +f 232/334/269 233/336/271 248/337/272 247/335/270 +f 233/336/271 234/338/273 249/339/274 248/337/272 +f 234/338/273 235/340/275 250/341/276 249/339/274 +f 235/340/275 236/342/277 251/343/278 250/341/276 +f 236/342/277 237/344/279 252/345/280 251/343/278 +f 238/319/254 239/318/253 254/346/281 253/347/282 +f 239/318/253 240/321/256 255/348/283 254/346/281 +f 240/321/256 241/323/258 256/349/284 255/348/283 +f 241/323/258 242/325/260 257/350/285 256/349/284 +f 242/325/260 243/327/262 258/351/286 257/350/285 +f 243/327/262 244/329/264 259/352/287 258/351/286 +f 244/329/264 245/331/266 260/353/288 259/352/287 +f 245/331/266 246/333/268 261/354/289 260/353/288 +f 246/333/268 247/335/270 262/355/290 261/354/289 +f 247/335/270 248/337/272 263/356/291 262/355/290 +f 248/337/272 249/339/274 264/357/292 263/356/291 +f 249/339/274 250/341/276 265/358/293 264/357/292 +f 250/341/276 251/343/278 266/359/294 265/358/293 +f 251/343/278 252/345/280 267/360/295 266/359/294 +f 253/347/282 254/346/281 269/361/296 268/362/297 +f 254/346/281 255/348/283 270/363/298 269/361/296 +f 255/348/283 256/349/284 271/364/299 270/363/298 +f 256/349/284 257/350/285 272/365/300 271/364/299 +f 257/350/285 258/351/286 273/366/301 272/365/300 +f 258/351/286 259/352/287 274/367/302 273/366/301 +f 259/352/287 260/353/288 275/368/303 274/367/302 +f 260/353/288 261/354/289 276/369/304 275/368/303 +f 261/354/289 262/355/290 277/370/305 276/369/304 +f 262/355/290 263/356/291 278/371/306 277/370/305 +f 263/356/291 264/357/292 279/372/307 278/371/306 +f 264/357/292 265/358/293 280/373/308 279/372/307 +f 265/358/293 266/359/294 281/374/309 280/373/308 +f 266/359/294 267/360/295 282/375/310 281/374/309 +f 268/362/297 269/361/296 284/376/311 283/377/312 +f 269/361/296 270/363/298 285/378/313 284/376/311 +f 270/363/298 271/364/299 286/379/314 285/378/313 +f 271/364/299 272/365/300 287/380/315 286/379/314 +f 272/365/300 273/366/301 288/381/316 287/380/315 +f 273/366/301 274/367/302 289/382/317 288/381/316 +f 274/367/302 275/368/303 290/383/318 289/382/317 +f 275/368/303 276/369/304 291/384/319 290/383/318 +f 276/369/304 277/370/305 292/385/320 291/384/319 +f 277/370/305 278/371/306 293/386/321 292/385/320 +f 278/371/306 279/372/307 294/387/322 293/386/321 +f 279/372/307 280/373/308 295/388/323 294/387/322 +f 280/373/308 281/374/309 296/389/324 295/388/323 +f 281/374/309 282/375/310 297/390/325 296/389/324 +f 283/377/312 284/376/311 299/391/326 298/392/327 +f 284/376/311 285/378/313 300/393/328 299/391/326 +f 285/378/313 286/379/314 301/394/329 300/393/328 +f 286/379/314 287/380/315 302/395/330 301/394/329 +f 287/380/315 288/381/316 303/396/331 302/395/330 +f 288/381/316 289/382/317 304/397/332 303/396/331 +f 289/382/317 290/383/318 305/398/333 304/397/332 +f 290/383/318 291/384/319 306/399/334 305/398/333 +f 291/384/319 292/385/320 307/400/335 306/399/334 +f 292/385/320 293/386/321 308/401/336 307/400/335 +f 293/386/321 294/387/322 309/402/337 308/401/336 +f 294/387/322 295/388/323 310/403/338 309/402/337 +f 295/388/323 296/389/324 311/404/339 310/403/338 +f 296/389/324 297/390/325 312/405/340 311/404/339 +f 298/392/327 299/391/326 314/406/341 313/407/342 +f 299/391/326 300/393/328 315/408/343 314/406/341 +f 300/393/328 301/394/329 316/409/344 315/408/343 +f 301/394/329 302/395/330 317/410/345 316/409/344 +f 302/395/330 303/396/331 318/411/346 317/410/345 +f 303/396/331 304/397/332 319/412/347 318/411/346 +f 304/397/332 305/398/333 320/413/348 319/412/347 +f 305/398/333 306/399/334 321/414/349 320/413/348 +f 306/399/334 307/400/335 322/415/350 321/414/349 +f 307/400/335 308/401/336 323/416/351 322/415/350 +f 308/401/336 309/402/337 324/417/352 323/416/351 +f 309/402/337 310/403/338 325/418/353 324/417/352 +f 310/403/338 311/404/339 326/419/354 325/418/353 +f 311/404/339 312/405/340 327/420/355 326/419/354 +f 313/407/342 314/406/341 329/421/356 328/422/357 +f 314/406/341 315/408/343 330/423/358 329/421/356 +f 315/408/343 316/409/344 331/424/359 330/423/358 +f 316/409/344 317/410/345 332/425/360 331/424/359 +f 317/410/345 318/411/346 333/426/361 332/425/360 +f 318/411/346 319/412/347 43/55/48 333/426/361 +f 319/412/347 320/413/348 44/58/51 43/55/48 +f 320/413/348 321/414/349 40/49/42 44/58/51 +f 321/414/349 322/415/350 334/427/362 40/49/42 +f 322/415/350 323/416/351 335/428/363 334/427/362 +f 323/416/351 324/417/352 336/429/364 335/428/363 +f 324/417/352 325/418/353 337/430/365 336/429/364 +f 325/418/353 326/419/354 338/431/366 337/430/365 +f 326/419/354 327/420/355 339/432/367 338/431/366 +f 328/422/357 329/421/356 341/433/368 340/434/369 +f 329/421/356 330/423/358 342/435/370 341/433/368 +f 330/423/358 331/424/359 343/436/371 342/435/370 +f 331/424/359 332/425/360 344/437/372 343/436/371 +f 332/425/360 333/426/361 345/438/373 344/437/372 +f 333/426/361 43/55/48 42/54/47 345/438/373 +f 40/49/42 334/427/362 346/439/374 41/51/44 +f 334/427/362 335/428/363 347/440/375 346/439/374 +f 335/428/363 336/429/364 348/441/376 347/440/375 +f 336/429/364 337/430/365 349/442/377 348/441/376 +f 337/430/365 338/431/366 350/443/378 349/442/377 +f 338/431/366 339/432/367 351/444/379 350/443/378 +f 340/434/369 341/433/368 353/445/380 352/446/381 +f 341/433/368 342/435/370 354/447/382 353/445/380 +f 342/435/370 343/436/371 355/448/383 354/447/382 +f 343/436/371 344/437/372 356/449/384 355/448/383 +f 344/437/372 345/438/373 357/450/385 356/449/384 +f 345/438/373 42/54/47 358/451/386 357/450/385 +f 42/54/47 45/60/53 359/452/387 358/451/386 +f 45/60/53 41/51/44 360/453/388 359/452/387 +f 41/51/44 346/439/374 361/454/389 360/453/388 +f 346/439/374 347/440/375 362/455/390 361/454/389 +f 347/440/375 348/441/376 363/456/391 362/455/390 +f 348/441/376 349/442/377 364/457/392 363/456/391 +f 349/442/377 350/443/378 365/458/393 364/457/392 +f 350/443/378 351/444/379 366/459/394 365/458/393 +f 352/446/381 353/445/380 368/460/395 367/461/396 +f 353/445/380 354/447/382 369/462/397 368/460/395 +f 354/447/382 355/448/383 370/463/398 369/462/397 +f 355/448/383 356/449/384 371/464/399 370/463/398 +f 356/449/384 357/450/385 372/465/400 371/464/399 +f 357/450/385 358/451/386 373/466/401 372/465/400 +f 358/451/386 359/452/387 374/467/402 373/466/401 +f 359/452/387 360/453/388 375/468/403 374/467/402 +f 360/453/388 361/454/389 376/469/404 375/468/403 +f 361/454/389 362/455/390 377/470/405 376/469/404 +f 362/455/390 363/456/391 378/471/406 377/470/405 +f 363/456/391 364/457/392 379/472/407 378/471/406 +f 364/457/392 365/458/393 380/473/408 379/472/407 +f 365/458/393 366/459/394 381/474/409 380/473/408 +f 367/461/396 368/460/395 382/475/410 +f 368/460/395 369/462/397 382/475/410 +f 369/462/397 370/463/398 382/475/410 +f 370/463/398 371/464/399 382/475/410 +f 371/464/399 372/465/400 382/475/410 +f 372/465/400 373/466/401 382/475/410 +f 373/466/401 374/467/402 382/475/410 +f 374/467/402 375/468/403 382/475/410 +f 375/468/403 376/469/404 382/475/410 +f 376/469/404 377/470/405 382/475/410 +f 377/470/405 378/471/406 382/475/410 +f 378/471/406 379/472/407 382/475/410 +f 379/472/407 380/473/408 382/475/410 +f 380/473/408 381/474/409 382/475/410 +f 223/316/251 238/319/254 396/476/411 383/477/412 +f 383/477/412 396/476/411 397/478/413 384/479/414 +f 384/479/414 397/478/413 398/480/415 385/481/416 +f 385/481/416 398/480/415 399/482/417 386/483/418 +f 386/483/418 399/482/417 400/484/419 387/485/420 +f 387/485/420 400/484/419 401/486/421 388/487/422 +f 388/487/422 401/486/421 402/488/423 389/489/424 +f 389/489/424 402/488/423 403/490/425 390/491/426 +f 390/491/426 403/490/425 404/492/427 391/493/428 +f 391/493/428 404/492/427 405/494/429 392/495/430 +f 392/495/430 405/494/429 406/496/431 393/497/432 +f 393/497/432 406/496/431 407/498/433 394/499/434 +f 394/499/434 407/498/433 408/500/435 395/501/436 +f 395/501/436 408/500/435 252/345/280 237/344/279 +f 238/319/254 253/347/282 409/502/437 396/476/411 +f 396/476/411 409/502/437 410/503/438 397/478/413 +f 397/478/413 410/503/438 411/504/439 398/480/415 +f 398/480/415 411/504/439 412/505/440 399/482/417 +f 399/482/417 412/505/440 413/506/441 400/484/419 +f 400/484/419 413/506/441 414/507/442 401/486/421 +f 401/486/421 414/507/442 415/508/443 402/488/423 +f 402/488/423 415/508/443 416/509/444 403/490/425 +f 403/490/425 416/509/444 417/510/445 404/492/427 +f 404/492/427 417/510/445 418/511/446 405/494/429 +f 405/494/429 418/511/446 419/512/447 406/496/431 +f 406/496/431 419/512/447 420/513/448 407/498/433 +f 407/498/433 420/513/448 421/514/449 408/500/435 +f 408/500/435 421/514/449 267/360/295 252/345/280 +f 253/347/282 268/362/297 422/515/450 409/502/437 +f 409/502/437 422/515/450 423/516/451 410/503/438 +f 410/503/438 423/516/451 424/517/452 411/504/439 +f 411/504/439 424/517/452 425/518/453 412/505/440 +f 412/505/440 425/518/453 426/519/454 413/506/441 +f 413/506/441 426/519/454 427/520/455 414/507/442 +f 414/507/442 427/520/455 428/521/456 415/508/443 +f 415/508/443 428/521/456 429/522/457 416/509/444 +f 416/509/444 429/522/457 430/523/458 417/510/445 +f 417/510/445 430/523/458 431/524/459 418/511/446 +f 418/511/446 431/524/459 432/525/460 419/512/447 +f 419/512/447 432/525/460 433/526/461 420/513/448 +f 420/513/448 433/526/461 434/527/462 421/514/449 +f 421/514/449 434/527/462 282/375/310 267/360/295 +f 268/362/297 283/377/312 435/528/463 422/515/450 +f 422/515/450 435/528/463 436/529/464 423/516/451 +f 423/516/451 436/529/464 437/530/465 424/517/452 +f 424/517/452 437/530/465 438/531/466 425/518/453 +f 425/518/453 438/531/466 439/532/467 426/519/454 +f 426/519/454 439/532/467 440/533/468 427/520/455 +f 427/520/455 440/533/468 441/534/469 428/521/456 +f 428/521/456 441/534/469 442/535/470 429/522/457 +f 429/522/457 442/535/470 443/536/471 430/523/458 +f 430/523/458 443/536/471 444/537/472 431/524/459 +f 431/524/459 444/537/472 445/538/473 432/525/460 +f 432/525/460 445/538/473 446/539/474 433/526/461 +f 433/526/461 446/539/474 447/540/475 434/527/462 +f 434/527/462 447/540/475 297/390/325 282/375/310 +f 283/377/312 298/392/327 448/541/476 435/528/463 +f 435/528/463 448/541/476 449/542/477 436/529/464 +f 436/529/464 449/542/477 450/543/478 437/530/465 +f 437/530/465 450/543/478 451/544/479 438/531/466 +f 438/531/466 451/544/479 452/545/480 439/532/467 +f 439/532/467 452/545/480 453/546/481 440/533/468 +f 440/533/468 453/546/481 454/547/482 441/534/469 +f 441/534/469 454/547/482 455/548/483 442/535/470 +f 442/535/470 455/548/483 456/549/484 443/536/471 +f 443/536/471 456/549/484 457/550/485 444/537/472 +f 444/537/472 457/550/485 458/551/486 445/538/473 +f 445/538/473 458/551/486 459/552/487 446/539/474 +f 446/539/474 459/552/487 460/553/488 447/540/475 +f 447/540/475 460/553/488 312/405/340 297/390/325 +f 298/392/327 313/407/342 461/554/489 448/541/476 +f 448/541/476 461/554/489 462/555/490 449/542/477 +f 449/542/477 462/555/490 463/556/491 450/543/478 +f 450/543/478 463/556/491 464/557/492 451/544/479 +f 451/544/479 464/557/492 465/558/493 452/545/480 +f 452/545/480 465/558/493 466/559/494 453/546/481 +f 453/546/481 466/559/494 467/560/495 454/547/482 +f 454/547/482 467/560/495 468/561/496 455/548/483 +f 455/548/483 468/561/496 469/562/497 456/549/484 +f 456/549/484 469/562/497 470/563/498 457/550/485 +f 457/550/485 470/563/498 471/564/499 458/551/486 +f 458/551/486 471/564/499 472/565/500 459/552/487 +f 459/552/487 472/565/500 473/566/501 460/553/488 +f 460/553/488 473/566/501 327/420/355 312/405/340 +f 313/407/342 328/422/357 474/567/502 461/554/489 +f 461/554/489 474/567/502 475/568/503 462/555/490 +f 462/555/490 475/568/503 476/569/504 463/556/491 +f 463/556/491 476/569/504 477/570/505 464/557/492 +f 464/557/492 477/570/505 478/571/506 465/558/493 +f 465/558/493 478/571/506 29/39/32 466/559/494 +f 466/559/494 29/39/32 30/44/37 467/560/495 +f 467/560/495 30/44/37 26/34/27 468/561/496 +f 468/561/496 26/34/27 479/572/507 469/562/497 +f 469/562/497 479/572/507 480/573/508 470/563/498 +f 470/563/498 480/573/508 481/574/509 471/564/499 +f 471/564/499 481/574/509 482/575/510 472/565/500 +f 472/565/500 482/575/510 483/576/511 473/566/501 +f 473/566/501 483/576/511 339/432/367 327/420/355 +f 328/422/357 340/434/369 484/577/512 474/567/502 +f 474/567/502 484/577/512 485/578/513 475/568/503 +f 475/568/503 485/578/513 486/579/514 476/569/504 +f 476/569/504 486/579/514 487/580/515 477/570/505 +f 477/570/505 487/580/515 488/581/516 478/571/506 +f 478/571/506 488/581/516 28/40/33 29/39/32 +f 26/34/27 27/37/30 489/582/517 479/572/507 +f 479/572/507 489/582/517 490/583/518 480/573/508 +f 480/573/508 490/583/518 491/584/519 481/574/509 +f 481/574/509 491/584/519 492/585/520 482/575/510 +f 482/575/510 492/585/520 493/586/521 483/576/511 +f 483/576/511 493/586/521 351/444/379 339/432/367 +f 340/434/369 352/446/381 494/587/522 484/577/512 +f 484/577/512 494/587/522 495/588/523 485/578/513 +f 485/578/513 495/588/523 496/589/524 486/579/514 +f 486/579/514 496/589/524 497/590/525 487/580/515 +f 487/580/515 497/590/525 498/591/526 488/581/516 +f 488/581/516 498/591/526 499/592/527 28/40/33 +f 28/40/33 499/592/527 500/593/528 31/46/39 +f 31/46/39 500/593/528 501/594/529 27/37/30 +f 27/37/30 501/594/529 502/595/530 489/582/517 +f 489/582/517 502/595/530 503/596/531 490/583/518 +f 490/583/518 503/596/531 504/597/532 491/584/519 +f 491/584/519 504/597/532 505/598/533 492/585/520 +f 492/585/520 505/598/533 506/599/534 493/586/521 +f 493/586/521 506/599/534 366/459/394 351/444/379 +f 352/446/381 367/461/396 507/600/535 494/587/522 +f 494/587/522 507/600/535 508/601/536 495/588/523 +f 495/588/523 508/601/536 509/602/537 496/589/524 +f 496/589/524 509/602/537 510/603/538 497/590/525 +f 497/590/525 510/603/538 511/604/539 498/591/526 +f 498/591/526 511/604/539 512/605/540 499/592/527 +f 499/592/527 512/605/540 513/606/541 500/593/528 +f 500/593/528 513/606/541 514/607/542 501/594/529 +f 501/594/529 514/607/542 515/608/543 502/595/530 +f 502/595/530 515/608/543 516/609/544 503/596/531 +f 503/596/531 516/609/544 517/610/545 504/597/532 +f 504/597/532 517/610/545 518/611/546 505/598/533 +f 505/598/533 518/611/546 519/612/547 506/599/534 +f 506/599/534 519/612/547 381/474/409 366/459/394 +f 367/461/396 382/475/410 507/600/535 +f 507/600/535 382/475/410 508/601/536 +f 508/601/536 382/475/410 509/602/537 +f 509/602/537 382/475/410 510/603/538 +f 510/603/538 382/475/410 511/604/539 +f 511/604/539 382/475/410 512/605/540 +f 512/605/540 382/475/410 513/606/541 +f 513/606/541 382/475/410 514/607/542 +f 514/607/542 382/475/410 515/608/543 +f 515/608/543 382/475/410 516/609/544 +f 516/609/544 382/475/410 517/610/545 +f 517/610/545 382/475/410 518/611/546 +f 518/611/546 382/475/410 519/612/547 +f 519/612/547 382/475/410 381/474/409 +f 522/613/548 521/614/549 520/615/550 523/616/551 +f 521/614/549 522/613/548 224/317/252 223/316/251 +f 522/613/548 524/617/552 225/320/255 224/317/252 +f 524/617/552 526/618/553 226/322/257 225/320/255 +f 526/618/553 528/619/554 227/324/259 226/322/257 +f 528/619/554 530/620/555 228/326/261 227/324/259 +f 530/620/555 532/621/556 229/328/263 228/326/261 +f 532/621/556 534/622/557 230/330/265 229/328/263 +f 534/622/557 536/623/558 231/332/267 230/330/265 +f 536/623/558 538/624/559 232/334/269 231/332/267 +f 538/624/559 540/625/560 233/336/271 232/334/269 +f 540/625/560 542/626/561 234/338/273 233/336/271 +f 542/626/561 544/627/562 235/340/275 234/338/273 +f 544/627/562 546/628/563 236/342/277 235/340/275 +f 546/628/563 548/629/564 237/344/279 236/342/277 +f 523/630/551 520/631/550 576/632/565 +f 525/633/566 523/630/551 576/632/565 +f 527/634/567 525/633/566 576/632/565 +f 529/635/568 527/634/567 576/632/565 +f 531/636/569 529/635/568 576/632/565 +f 533/637/570 531/636/569 576/632/565 +f 535/638/571 533/637/570 576/632/565 +f 537/639/572 535/638/571 576/632/565 +f 539/640/573 537/639/572 576/632/565 +f 541/641/574 539/640/573 576/632/565 +f 543/642/575 541/641/574 576/632/565 +f 545/643/576 543/642/575 576/632/565 +f 547/644/577 545/643/576 576/632/565 +f 549/645/578 547/644/577 576/632/565 +f 521/614/549 223/316/251 383/477/412 550/646/579 +f 550/646/579 383/477/412 384/479/414 552/647/580 +f 552/647/580 384/479/414 385/481/416 554/648/581 +f 554/648/581 385/481/416 386/483/418 556/649/582 +f 556/649/582 386/483/418 387/485/420 558/650/583 +f 558/650/583 387/485/420 388/487/422 560/651/584 +f 560/651/584 388/487/422 389/489/424 562/652/585 +f 562/652/585 389/489/424 390/491/426 564/653/586 +f 564/653/586 390/491/426 391/493/428 566/654/587 +f 566/654/587 391/493/428 392/495/430 568/655/588 +f 568/655/588 392/495/430 393/497/432 570/656/589 +f 570/656/589 393/497/432 394/499/434 572/657/590 +f 572/657/590 394/499/434 395/501/436 574/658/591 +f 574/658/591 395/501/436 237/344/279 548/629/564 +f 524/617/552 522/613/548 523/616/551 525/659/566 +f 526/618/553 524/617/552 525/659/566 527/660/567 +f 528/619/554 526/618/553 527/660/567 529/661/568 +f 530/620/555 528/619/554 529/661/568 531/662/569 +f 532/621/556 530/620/555 531/662/569 533/663/570 +f 534/622/557 532/621/556 533/663/570 535/664/571 +f 536/623/558 534/622/557 535/664/571 537/665/572 +f 538/624/559 536/623/558 537/665/572 539/666/573 +f 540/625/560 538/624/559 539/666/573 541/667/574 +f 542/626/561 540/625/560 541/667/574 543/668/575 +f 544/627/562 542/626/561 543/668/575 545/669/576 +f 546/628/563 544/627/562 545/669/576 547/670/577 +f 548/629/564 546/628/563 547/670/577 549/671/578 +f 574/658/591 548/629/564 549/671/578 575/672/592 +f 520/615/550 521/614/549 550/646/579 551/673/593 +f 550/646/579 552/647/580 553/674/594 551/673/593 +f 552/647/580 554/648/581 555/675/595 553/674/594 +f 554/648/581 556/649/582 557/676/596 555/675/595 +f 556/649/582 558/650/583 559/677/597 557/676/596 +f 558/650/583 560/651/584 561/678/598 559/677/597 +f 560/651/584 562/652/585 563/679/599 561/678/598 +f 562/652/585 564/653/586 565/680/600 563/679/599 +f 564/653/586 566/654/587 567/681/601 565/680/600 +f 566/654/587 568/655/588 569/682/602 567/681/601 +f 568/655/588 570/656/589 571/683/603 569/682/602 +f 570/656/589 572/657/590 573/684/604 571/683/603 +f 572/657/590 574/658/591 575/672/592 573/684/604 +f 551/685/593 576/632/565 520/631/550 +f 553/686/594 576/632/565 551/685/593 +f 555/687/595 576/632/565 553/686/594 +f 557/688/596 576/632/565 555/687/595 +f 559/689/597 576/632/565 557/688/596 +f 561/690/598 576/632/565 559/689/597 +f 563/691/599 576/632/565 561/690/598 +f 565/692/600 576/632/565 563/691/599 +f 567/693/601 576/632/565 565/692/600 +f 569/694/602 576/632/565 567/693/601 +f 571/695/603 576/632/565 569/694/602 +f 573/696/604 576/632/565 571/695/603 +f 575/697/592 576/632/565 573/696/604 +f 549/645/578 576/632/565 575/697/592 +s 6 +f 578/103/96 577/102/95 111/130/123 112/132/125 +f 579/104/97 578/103/96 112/132/125 113/134/127 +f 580/105/98 579/104/97 113/134/127 99/107/100 +f 581/91/84 580/105/98 99/107/100 100/106/99 +f 582/92/85 581/91/84 100/106/99 101/110/103 +f 583/93/86 582/92/85 101/110/103 102/112/105 +f 584/94/87 583/93/86 102/112/105 103/114/107 +f 585/164/157 584/94/87 103/114/107 156/182/175 +f 586/162/155 585/164/157 156/182/175 155/180/173 +f 587/163/156 586/162/155 155/180/173 154/178/171 +f 588/174/167 587/163/156 154/178/171 153/177/170 +f 589/173/166 588/174/167 153/177/170 165/200/193 +f 590/172/165 589/173/166 165/200/193 164/198/191 +f 591/171/164 590/172/165 164/198/191 163/196/189 +f 592/170/163 591/171/164 163/196/189 162/194/187 +f 593/169/162 592/170/163 162/194/187 161/192/185 +f 594/168/161 593/169/162 161/192/185 160/190/183 +f 595/167/160 594/168/161 160/190/183 159/188/181 +f 596/166/159 595/167/160 159/188/181 158/186/179 +f 597/165/158 596/166/159 158/186/179 157/184/177 +f 598/95/88 597/165/158 157/184/177 104/117/110 +f 599/96/89 598/95/88 104/117/110 105/116/109 +f 600/97/90 599/96/89 105/116/109 106/120/113 +f 601/98/91 600/97/90 106/120/113 107/122/115 +f 602/99/92 601/98/91 107/122/115 108/124/117 +f 603/100/93 602/99/92 108/124/117 109/126/119 +f 604/101/94 603/100/93 109/126/119 110/128/121 +f 577/102/95 604/101/94 110/128/121 111/130/123 +f 606/133/126 605/131/124 76/698/605 77/699/606 +f 607/135/128 606/133/126 77/699/606 78/700/607 +f 79/701/608 608/108/101 607/135/128 78/700/607 +f 609/109/102 608/108/101 79/701/608 80/702/609 +f 610/111/104 609/109/102 80/702/609 81/703/610 +f 611/113/106 610/111/104 81/703/610 82/704/611 +f 612/115/108 611/113/106 82/704/611 83/705/612 +f 613/181/174 612/115/108 83/705/612 139/706/613 +f 139/706/613 138/707/614 614/179/172 613/181/174 +f 138/707/614 137/708/615 615/175/168 614/179/172 +f 137/708/615 136/709/616 616/176/169 615/175/168 +f 617/199/192 616/176/169 136/709/616 135/710/617 +f 135/710/617 134/711/618 618/197/190 617/199/192 +f 134/711/618 133/712/619 619/195/188 618/197/190 +f 133/712/619 132/713/620 620/193/186 619/195/188 +f 132/713/620 131/714/621 621/191/184 620/193/186 +f 131/714/621 130/715/622 622/189/182 621/191/184 +f 130/715/622 129/716/623 623/187/180 622/189/182 +f 129/716/623 128/717/624 624/185/178 623/187/180 +f 128/717/624 127/718/625 625/183/176 624/185/178 +f 127/718/625 69/719/626 626/118/111 625/183/176 +f 627/119/112 626/118/111 69/719/626 70/720/627 +f 628/121/114 627/119/112 70/720/627 71/721/628 +f 629/123/116 628/121/114 71/721/628 72/722/629 +f 630/125/118 629/123/116 72/722/629 73/723/630 +f 631/127/120 630/125/118 73/723/630 74/724/631 +f 632/129/122 631/127/120 74/724/631 75/725/632 +f 605/131/124 632/129/122 75/725/632 76/698/605 +f 633/726/633 634/727/634 77/699/606 76/698/605 +f 634/727/634 635/728/635 78/700/607 77/699/606 +f 80/702/609 636/729/636 81/703/610 +f 636/729/636 637/730/637 82/704/611 81/703/610 +f 637/730/637 638/731/638 83/705/612 82/704/611 +f 638/731/638 639/732/639 645/733/640 83/705/612 +f 640/734/641 641/735/642 70/720/627 69/719/626 +f 641/735/642 642/736/643 71/721/628 70/720/627 +f 73/723/630 643/737/644 74/724/631 +f 643/737/644 644/738/645 75/725/632 74/724/631 +f 644/738/645 633/726/633 76/698/605 75/725/632 +f 639/732/639 640/734/641 69/719/626 645/733/640 +f 635/728/635 79/701/608 78/700/607 +f 635/728/635 636/729/636 80/702/609 79/701/608 +f 642/736/643 72/722/629 71/721/628 +f 642/736/643 643/737/644 73/723/630 72/722/629 +f 646/739/646 133/712/619 134/711/618 647/740/647 +f 647/740/647 134/711/618 135/710/617 648/741/648 +f 137/708/615 138/707/614 649/742/649 +f 649/742/649 138/707/614 139/706/613 650/743/650 +f 650/743/650 139/706/613 83/705/612 651/744/651 +f 651/744/651 83/705/612 645/733/640 652/745/652 +f 653/746/653 69/719/626 127/718/625 654/747/654 +f 654/747/654 127/718/625 128/717/624 655/748/655 +f 130/715/622 131/714/621 656/749/656 +f 656/749/656 131/714/621 132/713/620 657/750/657 +f 657/750/657 132/713/620 133/712/619 646/739/646 +f 652/745/652 645/733/640 69/719/626 653/746/653 +f 648/741/648 135/710/617 136/709/616 +f 648/741/648 136/709/616 137/708/615 649/742/649 +f 655/748/655 128/717/624 129/716/623 +f 655/748/655 129/716/623 130/715/622 656/749/656 +s 1 +f 2/2/2 698/751/658 697/752/659 3/3/5 +f 1/1/1 699/753/660 698/751/658 2/2/2 +f 8/8/15 700/754/661 699/753/660 1/1/1 +f 7/7/13 701/755/662 700/754/661 8/8/15 +f 694/756/663 701/755/662 7/7/13 6/6/11 +f 5/5/9 695/757/664 694/756/663 6/6/11 +f 4/758/7 696/759/665 695/757/664 5/5/9 +f 3/3/5 697/752/659 696/760/665 4/4/7 +s 6 +f 648/761/648 702/762/666 713/763/667 647/764/647 +f 649/765/649 703/766/668 702/762/666 648/761/648 +f 650/767/650 704/768/669 703/766/668 649/765/649 +f 651/769/651 705/770/670 704/768/669 650/767/650 +f 652/771/652 706/772/671 705/770/670 651/769/651 +f 713/763/667 712/773/672 646/774/646 647/764/647 +f 653/775/653 707/776/673 706/777/671 652/778/652 +f 654/779/654 708/780/674 707/776/673 653/775/653 +f 655/781/655 709/782/675 708/780/674 654/779/654 +f 656/783/656 710/784/676 709/782/675 655/781/655 +f 657/785/657 711/786/677 710/784/676 656/783/656 +f 646/774/646 712/773/672 711/786/677 657/785/657 +s 10 +f 671/787/678 683/788/679 682/789/680 670/790/681 +f 672/791/682 684/792/683 683/788/679 671/787/678 +f 673/793/684 685/794/685 684/792/683 672/791/682 +f 674/795/686 686/796/687 685/794/685 673/793/684 +f 675/797/688 687/798/689 686/796/687 674/795/686 +f 676/799/690 688/800/691 687/798/689 675/797/688 +f 677/801/692 689/802/693 688/800/691 676/799/690 +f 678/803/694 690/804/695 689/802/693 677/801/692 +f 679/805/696 691/806/697 690/804/695 678/803/694 +f 680/807/698 692/808/699 691/806/697 679/805/696 +f 681/809/700 693/810/701 692/808/699 680/807/698 +f 670/790/681 682/789/680 693/811/701 681/812/700 +s 1 +f 695/757/664 34/813/29 32/814/26 694/756/663 +f 696/759/665 39/815/38 34/813/29 695/757/664 +f 697/752/659 36/816/34 39/817/38 696/760/665 +f 698/751/658 35/818/31 36/816/34 697/752/659 +f 699/753/660 37/819/35 35/818/31 698/751/658 +f 700/754/661 38/820/36 37/819/35 699/753/660 +f 701/755/662 33/821/28 38/820/36 700/754/661 +f 32/814/26 33/821/28 701/755/662 694/756/663 +s 6 +f 661/822/702 660/823/703 702/762/666 703/766/668 +f 662/824/704 661/822/702 703/766/668 704/768/669 +f 663/825/705 662/824/704 704/768/669 705/770/670 +f 664/826/706 663/825/705 705/770/670 706/772/671 +f 665/827/707 664/828/706 706/777/671 707/776/673 +f 666/829/708 665/827/707 707/776/673 708/780/674 +f 667/830/709 666/829/708 708/780/674 709/782/675 +f 668/831/710 667/830/709 709/782/675 710/784/676 +f 669/832/711 668/831/710 710/784/676 711/786/677 +f 658/833/712 669/832/711 711/786/677 712/773/672 +f 659/834/713 658/833/712 712/773/672 713/763/667 +f 660/823/703 659/834/713 713/763/667 702/762/666 +s 1 +f 715/835/714 714/836/715 737/837/716 738/838/717 +f 716/839/718 715/835/714 738/838/717 739/840/719 +f 717/841/720 716/839/718 739/840/719 740/842/721 +f 718/843/722 717/841/720 740/842/721 741/844/723 +f 734/845/724 719/846/725 718/843/722 741/844/723 +f 720/847/726 719/846/725 734/845/724 735/848/727 +f 721/849/728 720/847/726 735/848/727 736/850/729 +f 714/836/715 721/851/728 736/852/729 737/837/716 +s 6 +f 635/853/635 634/854/634 753/855/730 742/856/731 +f 636/857/636 635/853/635 742/856/731 743/858/732 +f 637/859/637 636/857/636 743/858/732 744/860/733 +f 638/861/638 637/859/637 744/860/733 745/862/734 +f 639/863/639 638/861/638 745/862/734 746/864/735 +f 753/855/730 634/854/634 633/865/633 752/866/736 +f 640/867/641 639/868/639 746/869/735 747/870/737 +f 641/871/642 640/867/641 747/870/737 748/872/738 +f 642/873/643 641/871/642 748/872/738 749/874/739 +f 643/875/644 642/873/643 749/874/739 750/876/740 +f 644/877/645 643/875/644 750/876/740 751/878/741 +f 633/865/633 644/877/645 751/878/741 752/866/736 +s 1 +f 735/848/727 734/845/724 46/879/40 48/880/43 +f 736/850/729 735/848/727 48/880/43 53/881/52 +f 737/837/716 736/852/729 53/882/52 50/883/46 +f 738/838/717 737/837/716 50/883/46 49/884/45 +f 739/840/719 738/838/717 49/884/45 51/885/49 +f 740/842/721 739/840/719 51/885/49 52/886/50 +f 741/844/723 740/842/721 52/886/50 47/887/41 +f 46/879/40 734/845/724 741/844/723 47/887/41 +s 6 +f 725/888/742 743/858/732 742/856/731 724/889/743 +f 726/890/744 744/860/733 743/858/732 725/888/742 +f 727/891/745 745/862/734 744/860/733 726/890/744 +f 728/892/746 746/864/735 745/862/734 727/891/745 +f 729/893/747 747/870/737 746/869/735 728/894/746 +f 730/895/748 748/872/738 747/870/737 729/893/747 +f 731/896/749 749/874/739 748/872/738 730/895/748 +f 732/897/750 750/876/740 749/874/739 731/896/749 +f 733/898/751 751/878/741 750/876/740 732/897/750 +f 722/899/752 752/866/736 751/878/741 733/898/751 +f 723/900/753 753/855/730 752/866/736 722/899/752 +f 724/889/743 742/856/731 753/855/730 723/900/753 +s 1 +f 716/839/718 754/902/754 755/901/755 715/835/714 +f 715/835/714 755/901/755 756/903/756 714/836/715 +f 714/836/715 756/903/756 757/904/757 721/851/728 +f 721/849/728 757/925/757 758/905/758 720/847/726 +f 720/847/726 758/905/758 759/906/759 719/846/725 +f 719/846/725 759/906/759 760/907/760 718/843/722 +f 718/843/722 760/907/760 761/908/761 717/841/720 +f 717/841/720 761/908/761 754/902/754 716/839/718 +f 754/902/754 762/910/762 763/909/763 755/901/755 +f 755/901/755 763/909/763 764/911/764 756/903/756 +f 756/903/756 764/911/764 765/912/765 757/904/757 +f 757/925/757 765/926/765 766/913/766 758/905/758 +f 758/905/758 766/913/766 767/914/767 759/906/759 +f 759/906/759 767/914/767 768/915/768 760/907/760 +f 760/907/760 768/915/768 769/916/769 761/908/761 +f 761/908/761 769/916/769 762/910/762 754/902/754 +f 762/910/762 770/917/770 763/909/763 +f 763/909/763 770/918/770 764/911/764 +f 764/911/764 770/919/770 765/912/765 +f 765/926/765 770/920/770 766/913/766 +f 766/913/766 770/921/770 767/914/767 +f 767/914/767 770/922/770 768/915/768 +f 768/915/768 770/923/770 769/916/769 +f 769/916/769 770/924/770 762/910/762 +s 12 +f 772/929/771 771/930/772 783/931/773 784/932/774 +f 773/933/775 772/929/771 784/932/774 785/934/776 +f 774/935/777 773/933/775 785/934/776 786/936/778 +f 775/937/779 774/935/777 786/936/778 787/938/780 +f 776/939/781 775/937/779 787/938/780 788/940/782 +f 777/941/783 776/939/781 788/940/782 789/942/784 +f 778/943/785 777/941/783 789/942/784 790/944/786 +f 779/945/787 778/943/785 790/944/786 791/946/788 +f 780/947/789 779/945/787 791/946/788 792/948/790 +f 781/949/791 780/947/789 792/948/790 793/950/792 +f 782/951/793 781/949/791 793/950/792 794/952/794 +f 771/930/772 782/953/793 794/954/794 783/931/773 +s 6 +f 795/955/795 807/956/796 808/957/797 796/958/798 +f 796/958/798 808/957/797 809/959/799 797/960/800 +f 797/960/800 809/959/799 810/961/801 798/962/802 +f 798/962/802 810/961/801 811/963/803 799/964/804 +f 799/964/804 811/963/803 812/965/805 800/966/806 +f 800/967/806 812/968/805 813/969/807 801/970/808 +f 801/970/808 813/969/807 814/971/809 802/972/810 +f 802/972/810 814/971/809 815/973/811 803/974/812 +f 803/974/812 815/973/811 816/975/813 804/976/814 +f 804/976/814 816/975/813 817/977/815 805/978/816 +f 805/978/816 817/977/815 818/979/817 806/980/818 +f 806/980/818 818/979/817 807/956/796 795/955/795 +f 807/956/796 819/981/819 820/982/820 808/957/797 +f 808/957/797 820/982/820 821/983/821 809/959/799 +f 809/959/799 821/983/821 822/984/822 810/961/801 +f 810/961/801 822/984/822 823/985/823 811/963/803 +f 811/963/803 823/985/823 824/986/824 812/965/805 +f 812/968/805 824/987/824 825/988/825 813/969/807 +f 813/969/807 825/988/825 826/989/826 814/971/809 +f 814/971/809 826/989/826 827/990/827 815/973/811 +f 815/973/811 827/990/827 828/991/828 816/975/813 +f 816/975/813 828/991/828 829/992/829 817/977/815 +f 817/977/815 829/992/829 830/993/830 818/979/817 +f 818/979/817 830/993/830 819/981/819 807/956/796 +f 819/981/819 831/994/831 832/995/832 820/982/820 +f 820/982/820 832/995/832 833/996/833 821/983/821 +f 821/983/821 833/996/833 834/997/834 822/984/822 +f 822/984/822 834/997/834 835/998/835 823/985/823 +f 823/985/823 835/998/835 836/999/836 824/986/824 +f 824/987/824 836/1000/836 837/1001/837 825/988/825 +f 825/988/825 837/1001/837 838/1002/838 826/989/826 +f 826/989/826 838/1002/838 839/1003/839 827/990/827 +f 827/990/827 839/1003/839 840/1004/840 828/991/828 +f 828/991/828 840/1004/840 841/1005/841 829/992/829 +f 829/992/829 841/1005/841 842/1006/842 830/993/830 +f 830/993/830 842/1006/842 831/994/831 819/981/819 +f 831/994/831 843/1007/843 844/1008/844 832/995/832 +f 832/995/832 844/1008/844 845/1009/845 833/996/833 +f 833/996/833 845/1009/845 846/1010/846 834/997/834 +f 834/997/834 846/1010/846 847/1011/847 835/998/835 +f 835/998/835 847/1011/847 848/1012/848 836/999/836 +f 836/1000/836 848/1013/848 849/1014/849 837/1001/837 +f 837/1001/837 849/1014/849 850/1015/850 838/1002/838 +f 838/1002/838 850/1015/850 851/1016/851 839/1003/839 +f 839/1003/839 851/1016/851 852/1017/852 840/1004/840 +f 840/1004/840 852/1017/852 853/1018/853 841/1005/841 +f 841/1005/841 853/1018/853 854/1019/854 842/1006/842 +f 842/1006/842 854/1019/854 843/1007/843 831/994/831 +f 843/1007/843 733/898/751 732/897/750 844/1008/844 +f 844/1008/844 732/897/750 731/896/749 845/1009/845 +f 845/1009/845 731/896/749 730/895/748 846/1010/846 +f 846/1010/846 730/895/748 729/893/747 847/1011/847 +f 847/1011/847 729/893/747 728/894/746 848/1012/848 +f 848/1013/848 728/892/746 727/891/745 849/1014/849 +f 849/1014/849 727/891/745 726/890/744 850/1015/850 +f 850/1015/850 726/890/744 725/888/742 851/1016/851 +f 851/1016/851 725/888/742 724/889/743 852/1017/852 +f 852/1017/852 724/889/743 723/900/753 853/1018/853 +f 853/1018/853 723/900/753 722/899/752 854/1019/854 +f 854/1019/854 722/899/752 733/898/751 843/1007/843 +f 796/958/798 855/1020/855 795/955/795 +f 797/960/800 855/1021/855 796/958/798 +f 798/962/802 855/1022/855 797/960/800 +f 799/964/804 855/1023/855 798/962/802 +f 800/966/806 855/1024/855 799/964/804 +f 801/970/808 855/1025/855 800/967/806 +f 802/972/810 855/1026/855 801/970/808 +f 803/974/812 855/1027/855 802/972/810 +f 804/976/814 855/1028/855 803/974/812 +f 805/978/816 855/1029/855 804/976/814 +f 806/980/818 855/1030/855 805/978/816 +f 795/955/795 855/1031/855 806/980/818 +s 12 +f 856/1032/856 868/1033/857 869/1034/858 857/1035/859 +f 857/1035/859 869/1034/858 870/1036/860 858/1037/861 +f 858/1037/861 870/1036/860 871/1038/862 859/1039/863 +f 859/1039/863 871/1038/862 872/1040/864 860/1041/865 +f 860/1041/865 872/1040/864 873/1042/866 861/1043/867 +f 861/1044/867 873/1045/866 874/1046/868 862/1047/869 +f 862/1047/869 874/1046/868 875/1048/870 863/1049/871 +f 863/1049/871 875/1048/870 876/1050/872 864/1051/873 +f 864/1051/873 876/1050/872 877/1052/874 865/1053/875 +f 865/1053/875 877/1052/874 878/1054/876 866/1055/877 +f 866/1055/877 878/1054/876 879/1056/878 867/1057/879 +f 867/1057/879 879/1056/878 868/1033/857 856/1032/856 +f 868/1033/857 880/1058/880 881/1059/881 869/1034/858 +f 869/1034/858 881/1059/881 882/1060/882 870/1036/860 +f 870/1036/860 882/1060/882 883/1061/883 871/1038/862 +f 871/1038/862 883/1061/883 884/1062/884 872/1040/864 +f 872/1040/864 884/1062/884 885/1063/885 873/1042/866 +f 873/1045/866 885/1064/885 886/1065/886 874/1046/868 +f 874/1046/868 886/1065/886 887/1066/887 875/1048/870 +f 875/1048/870 887/1066/887 888/1067/888 876/1050/872 +f 876/1050/872 888/1067/888 889/1068/889 877/1052/874 +f 877/1052/874 889/1068/889 890/1069/890 878/1054/876 +f 878/1054/876 890/1069/890 891/1070/891 879/1056/878 +f 879/1056/878 891/1070/891 880/1058/880 868/1033/857 +f 880/1058/880 892/1071/892 893/1072/893 881/1059/881 +f 881/1059/881 893/1072/893 894/1073/894 882/1060/882 +f 882/1060/882 894/1073/894 895/1074/895 883/1061/883 +f 883/1061/883 895/1074/895 896/1075/896 884/1062/884 +f 884/1062/884 896/1075/896 897/1076/897 885/1063/885 +f 885/1064/885 897/1077/897 898/1078/898 886/1065/886 +f 886/1065/886 898/1078/898 899/1079/899 887/1066/887 +f 887/1066/887 899/1079/899 900/1080/900 888/1067/888 +f 888/1067/888 900/1080/900 901/1081/901 889/1068/889 +f 889/1068/889 901/1081/901 902/1082/902 890/1069/890 +f 890/1069/890 902/1082/902 903/1083/903 891/1070/891 +f 891/1070/891 903/1083/903 892/1071/892 880/1058/880 +f 892/1071/892 904/1084/904 905/1085/905 893/1072/893 +f 893/1072/893 905/1085/905 906/1086/906 894/1073/894 +f 894/1073/894 906/1086/906 907/1087/907 895/1074/895 +f 895/1074/895 907/1087/907 908/1088/908 896/1075/896 +f 896/1075/896 908/1088/908 909/1089/909 897/1076/897 +f 897/1077/897 909/1090/909 910/1091/910 898/1078/898 +f 898/1078/898 910/1091/910 911/1092/911 899/1079/899 +f 899/1079/899 911/1092/911 912/1093/912 900/1080/900 +f 900/1080/900 912/1093/912 913/1094/913 901/1081/901 +f 901/1081/901 913/1094/913 914/1095/914 902/1082/902 +f 902/1082/902 914/1095/914 915/1096/915 903/1083/903 +f 903/1083/903 915/1096/915 904/1084/904 892/1071/892 +f 904/1084/904 787/938/780 786/936/778 905/1085/905 +f 905/1085/905 786/936/778 785/934/776 906/1086/906 +f 906/1086/906 785/934/776 784/932/774 907/1087/907 +f 907/1087/907 784/932/774 783/931/773 908/1088/908 +f 908/1088/908 783/931/773 794/954/794 909/1089/909 +f 909/1090/909 794/952/794 793/950/792 910/1091/910 +f 910/1091/910 793/950/792 792/948/790 911/1092/911 +f 911/1092/911 792/948/790 791/946/788 912/1093/912 +f 912/1093/912 791/946/788 790/944/786 913/1094/913 +f 913/1094/913 790/944/786 789/942/784 914/1095/914 +f 914/1095/914 789/942/784 788/940/782 915/1096/915 +f 915/1096/915 788/940/782 787/938/780 904/1084/904 +f 857/1035/859 916/1097/916 856/1032/856 +f 858/1037/861 916/1098/916 857/1035/859 +f 859/1039/863 916/1099/916 858/1037/861 +f 860/1041/865 916/1100/916 859/1039/863 +f 861/1043/867 916/1101/916 860/1041/865 +f 862/1047/869 916/1102/916 861/1044/867 +f 863/1049/871 916/1103/916 862/1047/869 +f 864/1051/873 916/1104/916 863/1049/871 +f 865/1053/875 916/1105/916 864/1051/873 +f 866/1055/877 916/1106/916 865/1053/875 +f 867/1057/879 916/1107/916 866/1055/877 +f 856/1032/856 916/1108/916 867/1057/879 +f 917/1109/917 918/1110/918 930/1111/919 929/1112/920 +f 918/1110/918 919/1113/921 931/1114/922 930/1111/919 +f 919/1113/921 920/1115/923 932/1116/924 931/1114/922 +f 920/1115/923 921/1117/925 933/1118/926 932/1116/924 +f 921/1117/925 922/1119/927 934/1120/928 933/1118/926 +f 922/1121/927 923/1122/929 935/1123/930 934/1124/928 +f 923/1122/929 924/1125/931 936/1126/932 935/1123/930 +f 924/1125/931 925/1127/933 937/1128/934 936/1126/932 +f 925/1127/933 926/1129/935 938/1130/936 937/1128/934 +f 926/1129/935 927/1131/937 939/1132/938 938/1130/936 +f 927/1131/937 928/1133/939 940/1134/940 939/1132/938 +f 928/1133/939 917/1109/917 929/1112/920 940/1134/940 +f 929/1112/920 930/1111/919 942/1135/941 941/1136/942 +f 930/1111/919 931/1114/922 943/1137/943 942/1135/941 +f 931/1114/922 932/1116/924 944/1138/944 943/1137/943 +f 932/1116/924 933/1118/926 945/1139/945 944/1138/944 +f 933/1118/926 934/1120/928 946/1140/946 945/1139/945 +f 934/1124/928 935/1123/930 947/1141/947 946/1142/946 +f 935/1123/930 936/1126/932 948/1143/948 947/1141/947 +f 936/1126/932 937/1128/934 949/1144/949 948/1143/948 +f 937/1128/934 938/1130/936 950/1145/950 949/1144/949 +f 938/1130/936 939/1132/938 951/1146/951 950/1145/950 +f 939/1132/938 940/1134/940 952/1147/952 951/1146/951 +f 940/1134/940 929/1112/920 941/1136/942 952/1147/952 +f 941/1136/942 942/1135/941 954/1148/953 953/1149/954 +f 942/1135/941 943/1137/943 955/1150/955 954/1148/953 +f 943/1137/943 944/1138/944 956/1151/956 955/1150/955 +f 944/1138/944 945/1139/945 957/1152/957 956/1151/956 +f 945/1139/945 946/1140/946 958/1153/958 957/1152/957 +f 946/1142/946 947/1141/947 959/1154/959 958/1155/958 +f 947/1141/947 948/1143/948 960/1156/960 959/1154/959 +f 948/1143/948 949/1144/949 961/1157/961 960/1156/960 +f 949/1144/949 950/1145/950 962/1158/962 961/1157/961 +f 950/1145/950 951/1146/951 963/1159/963 962/1158/962 +f 951/1146/951 952/1147/952 964/1160/964 963/1159/963 +f 952/1147/952 941/1136/942 953/1149/954 964/1160/964 +f 953/1149/954 954/1148/953 966/1161/965 965/1162/966 +f 954/1148/953 955/1150/955 967/1163/967 966/1161/965 +f 955/1150/955 956/1151/956 968/1164/968 967/1163/967 +f 956/1151/956 957/1152/957 969/1165/969 968/1164/968 +f 957/1152/957 958/1153/958 970/1166/970 969/1165/969 +f 958/1155/958 959/1154/959 971/1167/971 970/1168/970 +f 959/1154/959 960/1156/960 972/1169/972 971/1167/971 +f 960/1156/960 961/1157/961 973/1170/973 972/1169/972 +f 961/1157/961 962/1158/962 974/1171/974 973/1170/973 +f 962/1158/962 963/1159/963 975/1172/975 974/1171/974 +f 963/1159/963 964/1160/964 976/1173/976 975/1172/975 +f 964/1160/964 953/1149/954 965/1162/966 976/1173/976 +f 965/1162/966 966/1161/965 774/935/777 775/937/779 +f 966/1161/965 967/1163/967 773/933/775 774/935/777 +f 967/1163/967 968/1164/968 772/929/771 773/933/775 +f 968/1164/968 969/1165/969 771/930/772 772/929/771 +f 969/1165/969 970/1166/970 782/953/793 771/930/772 +f 970/1168/970 971/1167/971 781/949/791 782/951/793 +f 971/1167/971 972/1169/972 780/947/789 781/949/791 +f 972/1169/972 973/1170/973 779/945/787 780/947/789 +f 973/1170/973 974/1171/974 778/943/785 779/945/787 +f 974/1171/974 975/1172/975 777/941/783 778/943/785 +f 975/1172/975 976/1173/976 776/939/781 777/941/783 +f 976/1173/976 965/1162/966 775/937/779 776/939/781 +f 918/1110/918 917/1109/917 977/1174/977 +f 919/1113/921 918/1110/918 977/1175/977 +f 920/1115/923 919/1113/921 977/1176/977 +f 921/1117/925 920/1115/923 977/1177/977 +f 922/1119/927 921/1117/925 977/1178/977 +f 923/1122/929 922/1121/927 977/1179/977 +f 924/1125/931 923/1122/929 977/1180/977 +f 925/1127/933 924/1125/931 977/1181/977 +f 926/1129/935 925/1127/933 977/1182/977 +f 927/1131/937 926/1129/935 977/1183/977 +f 928/1133/939 927/1131/937 977/1184/977 +f 917/1109/917 928/1133/939 977/1185/977 +s 6 +f 978/1186/978 979/1187/979 991/1188/980 990/1189/981 +f 979/1187/979 980/1190/982 992/1191/983 991/1188/980 +f 980/1190/982 981/1192/984 993/1193/985 992/1191/983 +f 981/1192/984 982/1194/986 994/1195/987 993/1193/985 +f 982/1194/986 983/1196/988 995/1197/989 994/1195/987 +f 983/1198/988 984/1199/990 996/1200/991 995/1201/989 +f 984/1199/990 985/1202/992 997/1203/993 996/1200/991 +f 985/1202/992 986/1204/994 998/1205/995 997/1203/993 +f 986/1204/994 987/1206/996 999/1207/997 998/1205/995 +f 987/1206/996 988/1208/998 1000/1209/999 999/1207/997 +f 988/1208/998 989/1210/1000 1001/1211/1001 1000/1209/999 +f 989/1210/1000 978/1186/978 990/1189/981 1001/1211/1001 +f 990/1189/981 991/1188/980 1003/1212/1002 1002/1213/1003 +f 991/1188/980 992/1191/983 1004/1214/1004 1003/1212/1002 +f 992/1191/983 993/1193/985 1005/1215/1005 1004/1214/1004 +f 993/1193/985 994/1195/987 1006/1216/1006 1005/1215/1005 +f 994/1195/987 995/1197/989 1007/1217/1007 1006/1216/1006 +f 995/1201/989 996/1200/991 1008/1218/1008 1007/1219/1007 +f 996/1200/991 997/1203/993 1009/1220/1009 1008/1218/1008 +f 997/1203/993 998/1205/995 1010/1221/1010 1009/1220/1009 +f 998/1205/995 999/1207/997 1011/1222/1011 1010/1221/1010 +f 999/1207/997 1000/1209/999 1012/1223/1012 1011/1222/1011 +f 1000/1209/999 1001/1211/1001 1013/1224/1013 1012/1223/1012 +f 1001/1211/1001 990/1189/981 1002/1213/1003 1013/1224/1013 +f 1002/1213/1003 1003/1212/1002 1015/1225/1014 1014/1226/1015 +f 1003/1212/1002 1004/1214/1004 1016/1227/1016 1015/1225/1014 +f 1004/1214/1004 1005/1215/1005 1017/1228/1017 1016/1227/1016 +f 1005/1215/1005 1006/1216/1006 1018/1229/1018 1017/1228/1017 +f 1006/1216/1006 1007/1217/1007 1019/1230/1019 1018/1229/1018 +f 1007/1219/1007 1008/1218/1008 1020/1231/1020 1019/1232/1019 +f 1008/1218/1008 1009/1220/1009 1021/1233/1021 1020/1231/1020 +f 1009/1220/1009 1010/1221/1010 1022/1234/1022 1021/1233/1021 +f 1010/1221/1010 1011/1222/1011 1023/1235/1023 1022/1234/1022 +f 1011/1222/1011 1012/1223/1012 1024/1236/1024 1023/1235/1023 +f 1012/1223/1012 1013/1224/1013 1025/1237/1025 1024/1236/1024 +f 1013/1224/1013 1002/1213/1003 1014/1226/1015 1025/1237/1025 +f 1014/1226/1015 1015/1225/1014 1027/1238/1026 1026/1239/1027 +f 1015/1225/1014 1016/1227/1016 1028/1240/1028 1027/1238/1026 +f 1016/1227/1016 1017/1228/1017 1029/1241/1029 1028/1240/1028 +f 1017/1228/1017 1018/1229/1018 1030/1242/1030 1029/1241/1029 +f 1018/1229/1018 1019/1230/1019 1031/1243/1031 1030/1242/1030 +f 1019/1232/1019 1020/1231/1020 1032/1244/1032 1031/1245/1031 +f 1020/1231/1020 1021/1233/1021 1033/1246/1033 1032/1244/1032 +f 1021/1233/1021 1022/1234/1022 1034/1247/1034 1033/1246/1033 +f 1022/1234/1022 1023/1235/1023 1035/1248/1035 1034/1247/1034 +f 1023/1235/1023 1024/1236/1024 1036/1249/1036 1035/1248/1035 +f 1024/1236/1024 1025/1237/1025 1037/1250/1037 1036/1249/1036 +f 1025/1237/1025 1014/1226/1015 1026/1239/1027 1037/1250/1037 +f 1026/1239/1027 1027/1238/1026 668/831/710 669/832/711 +f 1027/1238/1026 1028/1240/1028 667/830/709 668/831/710 +f 1028/1240/1028 1029/1241/1029 666/829/708 667/830/709 +f 1029/1241/1029 1030/1242/1030 665/827/707 666/829/708 +f 1030/1242/1030 1031/1243/1031 664/828/706 665/827/707 +f 1031/1245/1031 1032/1244/1032 663/825/705 664/826/706 +f 1032/1244/1032 1033/1246/1033 662/824/704 663/825/705 +f 1033/1246/1033 1034/1247/1034 661/822/702 662/824/704 +f 1034/1247/1034 1035/1248/1035 660/823/703 661/822/702 +f 1035/1248/1035 1036/1249/1036 659/834/713 660/823/703 +f 1036/1249/1036 1037/1250/1037 658/833/712 659/834/713 +f 1037/1250/1037 1026/1239/1027 669/832/711 658/833/712 +f 979/1187/979 978/1186/978 1038/1251/1038 +f 980/1190/982 979/1187/979 1038/1252/1038 +f 981/1192/984 980/1190/982 1038/1253/1038 +f 982/1194/986 981/1192/984 1038/1254/1038 +f 983/1196/988 982/1194/986 1038/1255/1038 +f 984/1199/990 983/1198/988 1038/1256/1038 +f 985/1202/992 984/1199/990 1038/1257/1038 +f 986/1204/994 985/1202/992 1038/1258/1038 +f 987/1206/996 986/1204/994 1038/1259/1038 +f 988/1208/998 987/1206/996 1038/1260/1038 +f 989/1210/1000 988/1208/998 1038/1261/1038 +f 978/1186/978 989/1210/1000 1038/1262/1038 +s 10 +f 1039/1263/1039 1040/1264/1040 1052/1265/1041 1051/1266/1042 +f 1040/1264/1040 1041/1267/1043 1053/1268/1044 1052/1265/1041 +f 1041/1267/1043 1042/1269/1045 1054/1270/1046 1053/1268/1044 +f 1042/1269/1045 1043/1271/1047 1055/1272/1048 1054/1270/1046 +f 1043/1271/1047 1044/1273/1049 1056/1274/1050 1055/1272/1048 +f 1044/1275/1049 1045/1276/1051 1057/1277/1052 1056/1278/1050 +f 1045/1276/1051 1046/1279/1053 1058/1280/1054 1057/1277/1052 +f 1046/1279/1053 1047/1281/1055 1059/1282/1056 1058/1280/1054 +f 1047/1281/1055 1048/1283/1057 1060/1284/1058 1059/1282/1056 +f 1048/1283/1057 1049/1285/1059 1061/1286/1060 1060/1284/1058 +f 1049/1285/1059 1050/1287/1061 1062/1288/1062 1061/1286/1060 +f 1050/1287/1061 1039/1263/1039 1051/1266/1042 1062/1288/1062 +f 1051/1266/1042 1052/1265/1041 1064/1289/1063 1063/1290/1064 +f 1052/1265/1041 1053/1268/1044 1065/1291/1065 1064/1289/1063 +f 1053/1268/1044 1054/1270/1046 1066/1292/1066 1065/1291/1065 +f 1054/1270/1046 1055/1272/1048 1067/1293/1067 1066/1292/1066 +f 1055/1272/1048 1056/1274/1050 1068/1294/1068 1067/1293/1067 +f 1056/1278/1050 1057/1277/1052 1069/1295/1069 1068/1296/1068 +f 1057/1277/1052 1058/1280/1054 1070/1297/1070 1069/1295/1069 +f 1058/1280/1054 1059/1282/1056 1071/1298/1071 1070/1297/1070 +f 1059/1282/1056 1060/1284/1058 1072/1299/1072 1071/1298/1071 +f 1060/1284/1058 1061/1286/1060 1073/1300/1073 1072/1299/1072 +f 1061/1286/1060 1062/1288/1062 1074/1301/1074 1073/1300/1073 +f 1062/1288/1062 1051/1266/1042 1063/1290/1064 1074/1301/1074 +f 1063/1290/1064 1064/1289/1063 1076/1302/1075 1075/1303/1076 +f 1064/1289/1063 1065/1291/1065 1077/1304/1077 1076/1302/1075 +f 1065/1291/1065 1066/1292/1066 1078/1305/1078 1077/1304/1077 +f 1066/1292/1066 1067/1293/1067 1079/1306/1079 1078/1305/1078 +f 1067/1293/1067 1068/1294/1068 1080/1307/1080 1079/1306/1079 +f 1068/1296/1068 1069/1295/1069 1081/1308/1081 1080/1309/1080 +f 1069/1295/1069 1070/1297/1070 1082/1310/1082 1081/1308/1081 +f 1070/1297/1070 1071/1298/1071 1083/1311/1083 1082/1310/1082 +f 1071/1298/1071 1072/1299/1072 1084/1312/1084 1083/1311/1083 +f 1072/1299/1072 1073/1300/1073 1085/1313/1085 1084/1312/1084 +f 1073/1300/1073 1074/1301/1074 1086/1314/1086 1085/1313/1085 +f 1074/1301/1074 1063/1290/1064 1075/1303/1076 1086/1314/1086 +f 1075/1303/1076 1076/1302/1075 1088/1315/1087 1087/1316/1088 +f 1076/1302/1075 1077/1304/1077 1089/1317/1089 1088/1315/1087 +f 1077/1304/1077 1078/1305/1078 1090/1318/1090 1089/1317/1089 +f 1078/1305/1078 1079/1306/1079 1091/1319/1091 1090/1318/1090 +f 1079/1306/1079 1080/1307/1080 1092/1320/1092 1091/1319/1091 +f 1080/1309/1080 1081/1308/1081 1093/1321/1093 1092/1322/1092 +f 1081/1308/1081 1082/1310/1082 1094/1323/1094 1093/1321/1093 +f 1082/1310/1082 1083/1311/1083 1095/1324/1095 1094/1323/1094 +f 1083/1311/1083 1084/1312/1084 1096/1325/1096 1095/1324/1095 +f 1084/1312/1084 1085/1313/1085 1097/1326/1097 1096/1325/1096 +f 1085/1313/1085 1086/1314/1086 1098/1327/1098 1097/1326/1097 +f 1086/1314/1086 1075/1303/1076 1087/1316/1088 1098/1327/1098 +f 1087/1316/1088 1088/1315/1087 685/794/685 686/796/687 +f 1088/1315/1087 1089/1317/1089 684/792/683 685/794/685 +f 1089/1317/1089 1090/1318/1090 683/788/679 684/792/683 +f 1090/1318/1090 1091/1319/1091 682/789/680 683/788/679 +f 1091/1319/1091 1092/1320/1092 693/811/701 682/789/680 +f 1092/1322/1092 1093/1321/1093 692/808/699 693/810/701 +f 1093/1321/1093 1094/1323/1094 691/806/697 692/808/699 +f 1094/1323/1094 1095/1324/1095 690/804/695 691/806/697 +f 1095/1324/1095 1096/1325/1096 689/802/693 690/804/695 +f 1096/1325/1096 1097/1326/1097 688/800/691 689/802/693 +f 1097/1326/1097 1098/1327/1098 687/798/689 688/800/691 +f 1098/1327/1098 1087/1316/1088 686/796/687 687/798/689 +f 1040/1264/1040 1039/1263/1039 1099/1328/1099 +f 1041/1267/1043 1040/1264/1040 1099/1329/1099 +f 1042/1269/1045 1041/1267/1043 1099/1330/1099 +f 1043/1271/1047 1042/1269/1045 1099/1331/1099 +f 1044/1273/1049 1043/1271/1047 1099/1332/1099 +f 1045/1276/1051 1044/1275/1049 1099/1333/1099 +f 1046/1279/1053 1045/1276/1051 1099/1334/1099 +f 1047/1281/1055 1046/1279/1053 1099/1335/1099 +f 1048/1283/1057 1047/1281/1055 1099/1336/1099 +f 1049/1285/1059 1048/1283/1057 1099/1337/1099 +f 1050/1287/1061 1049/1285/1059 1099/1338/1099 +f 1039/1263/1039 1050/1287/1061 1099/1339/1099 +f 1100/1340/1100 1112/1341/1101 1113/1342/1102 1101/1343/1103 +f 1101/1343/1103 1113/1342/1102 1114/1344/1104 1102/1345/1105 +f 1102/1345/1105 1114/1344/1104 1115/1346/1106 1103/1347/1107 +f 1103/1347/1107 1115/1346/1106 1116/1348/1108 1104/1349/1109 +f 1104/1349/1109 1116/1348/1108 1117/1350/1110 1105/1351/1111 +f 1105/1352/1111 1117/1353/1110 1118/1354/1112 1106/1355/1113 +f 1106/1355/1113 1118/1354/1112 1119/1356/1114 1107/1357/1115 +f 1107/1357/1115 1119/1356/1114 1120/1358/1116 1108/1359/1117 +f 1108/1359/1117 1120/1358/1116 1121/1360/1118 1109/1361/1119 +f 1109/1361/1119 1121/1360/1118 1122/1362/1120 1110/1363/1121 +f 1110/1363/1121 1122/1362/1120 1123/1364/1122 1111/1365/1123 +f 1111/1365/1123 1123/1364/1122 1112/1341/1101 1100/1340/1100 +f 1112/1341/1101 1124/1366/1124 1125/1367/1125 1113/1342/1102 +f 1113/1342/1102 1125/1367/1125 1126/1368/1126 1114/1344/1104 +f 1114/1344/1104 1126/1368/1126 1127/1369/1127 1115/1346/1106 +f 1115/1346/1106 1127/1369/1127 1128/1370/1128 1116/1348/1108 +f 1116/1348/1108 1128/1370/1128 1129/1371/1129 1117/1350/1110 +f 1117/1353/1110 1129/1372/1129 1130/1373/1130 1118/1354/1112 +f 1118/1354/1112 1130/1373/1130 1131/1374/1131 1119/1356/1114 +f 1119/1356/1114 1131/1374/1131 1132/1375/1132 1120/1358/1116 +f 1120/1358/1116 1132/1375/1132 1133/1376/1133 1121/1360/1118 +f 1121/1360/1118 1133/1376/1133 1134/1377/1134 1122/1362/1120 +f 1122/1362/1120 1134/1377/1134 1135/1378/1135 1123/1364/1122 +f 1123/1364/1122 1135/1378/1135 1124/1366/1124 1112/1341/1101 +f 1124/1366/1124 1136/1379/1136 1137/1380/1137 1125/1367/1125 +f 1125/1367/1125 1137/1380/1137 1138/1381/1138 1126/1368/1126 +f 1126/1368/1126 1138/1381/1138 1139/1382/1139 1127/1369/1127 +f 1127/1369/1127 1139/1382/1139 1140/1383/1140 1128/1370/1128 +f 1128/1370/1128 1140/1383/1140 1141/1384/1141 1129/1371/1129 +f 1129/1372/1129 1141/1385/1141 1142/1386/1142 1130/1373/1130 +f 1130/1373/1130 1142/1386/1142 1143/1387/1143 1131/1374/1131 +f 1131/1374/1131 1143/1387/1143 1144/1388/1144 1132/1375/1132 +f 1132/1375/1132 1144/1388/1144 1145/1389/1145 1133/1376/1133 +f 1133/1376/1133 1145/1389/1145 1146/1390/1146 1134/1377/1134 +f 1134/1377/1134 1146/1390/1146 1147/1391/1147 1135/1378/1135 +f 1135/1378/1135 1147/1391/1147 1136/1379/1136 1124/1366/1124 +f 1136/1379/1136 1148/1392/1148 1149/1393/1149 1137/1380/1137 +f 1137/1380/1137 1149/1393/1149 1150/1394/1150 1138/1381/1138 +f 1138/1381/1138 1150/1394/1150 1151/1395/1151 1139/1382/1139 +f 1139/1382/1139 1151/1395/1151 1152/1396/1152 1140/1383/1140 +f 1140/1383/1140 1152/1396/1152 1153/1397/1153 1141/1384/1141 +f 1141/1385/1141 1153/1398/1153 1154/1399/1154 1142/1386/1142 +f 1142/1386/1142 1154/1399/1154 1155/1400/1155 1143/1387/1143 +f 1143/1387/1143 1155/1400/1155 1156/1401/1156 1144/1388/1144 +f 1144/1388/1144 1156/1401/1156 1157/1402/1157 1145/1389/1145 +f 1145/1389/1145 1157/1402/1157 1158/1403/1158 1146/1390/1146 +f 1146/1390/1146 1158/1403/1158 1159/1404/1159 1147/1391/1147 +f 1147/1391/1147 1159/1404/1159 1148/1392/1148 1136/1379/1136 +f 1148/1392/1148 674/795/686 673/793/684 1149/1393/1149 +f 1149/1393/1149 673/793/684 672/791/682 1150/1394/1150 +f 1150/1394/1150 672/791/682 671/787/678 1151/1395/1151 +f 1151/1395/1151 671/787/678 670/790/681 1152/1396/1152 +f 1152/1396/1152 670/790/681 681/812/700 1153/1397/1153 +f 1153/1398/1153 681/809/700 680/807/698 1154/1399/1154 +f 1154/1399/1154 680/807/698 679/805/696 1155/1400/1155 +f 1155/1400/1155 679/805/696 678/803/694 1156/1401/1156 +f 1156/1401/1156 678/803/694 677/801/692 1157/1402/1157 +f 1157/1402/1157 677/801/692 676/799/690 1158/1403/1158 +f 1158/1403/1158 676/799/690 675/797/688 1159/1404/1159 +f 1159/1404/1159 675/797/688 674/795/686 1148/1392/1148 +f 1101/1343/1103 1160/1405/1160 1100/1340/1100 +f 1102/1345/1105 1160/1406/1160 1101/1343/1103 +f 1103/1347/1107 1160/1407/1160 1102/1345/1105 +f 1104/1349/1109 1160/1408/1160 1103/1347/1107 +f 1105/1351/1111 1160/1409/1160 1104/1349/1109 +f 1106/1355/1113 1160/1410/1160 1105/1352/1111 +f 1107/1357/1115 1160/1411/1160 1106/1355/1113 +f 1108/1359/1117 1160/1412/1160 1107/1357/1115 +f 1109/1361/1119 1160/1413/1160 1108/1359/1117 +f 1110/1363/1121 1160/1414/1160 1109/1361/1119 +f 1111/1365/1123 1160/1415/1160 1110/1363/1121 +f 1100/1340/1100 1160/1416/1160 1111/1365/1123 diff --git a/app/src/main/assets/models/andy.png b/app/src/main/assets/models/andy.png new file mode 100644 index 0000000..f88b1d6 --- /dev/null +++ b/app/src/main/assets/models/andy.png Binary files differ diff --git a/app/src/main/assets/models/andy_shadow.obj b/app/src/main/assets/models/andy_shadow.obj new file mode 100644 index 0000000..bbc1908 --- /dev/null +++ b/app/src/main/assets/models/andy_shadow.obj @@ -0,0 +1,18 @@ +# This file uses centimeters as units for non-parametric coordinates. + +g default +v -0.100000 -0.000000 0.100000 +v 0.100000 -0.000000 0.100000 +v -0.100000 0.000000 -0.100000 +v 0.100000 0.000000 -0.100000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +s off +g AndyBlobShadow_GEO +f 4/4/1 3/3/2 1/1/3 2/2/4 diff --git a/app/src/main/assets/models/andy_shadow.png b/app/src/main/assets/models/andy_shadow.png new file mode 100644 index 0000000..45a09ce --- /dev/null +++ b/app/src/main/assets/models/andy_shadow.png Binary files differ diff --git a/app/src/main/assets/models/andy_spec.png b/app/src/main/assets/models/andy_spec.png new file mode 100644 index 0000000..921e605 --- /dev/null +++ b/app/src/main/assets/models/andy_spec.png Binary files differ diff --git a/app/src/main/assets/models/trigrid.png b/app/src/main/assets/models/trigrid.png new file mode 100644 index 0000000..05cbe6e --- /dev/null +++ b/app/src/main/assets/models/trigrid.png Binary files differ diff --git a/app/src/main/assets/shaders/object.frag b/app/src/main/assets/shaders/object.frag new file mode 100644 index 0000000..e5dc3af --- /dev/null +++ b/app/src/main/assets/shaders/object.frag @@ -0,0 +1,82 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +precision mediump float; + +uniform sampler2D u_Texture; + +uniform vec4 u_LightingParameters; +uniform vec4 u_MaterialParameters; +uniform vec4 u_ColorCorrectionParameters; + +varying vec3 v_ViewPosition; +varying vec3 v_ViewNormal; +varying vec2 v_TexCoord; +uniform vec4 u_ObjColor; + +void main() { + // We support approximate sRGB gamma. + const float kGamma = 0.4545454; + const float kInverseGamma = 2.2; + const float kMiddleGrayGamma = 0.466; + + // Unpack lighting and material parameters for better naming. + vec3 viewLightDirection = u_LightingParameters.xyz; + vec3 colorShift = u_ColorCorrectionParameters.rgb; + float averagePixelIntensity = u_ColorCorrectionParameters.a; + + float materialAmbient = u_MaterialParameters.x; + float materialDiffuse = u_MaterialParameters.y; + float materialSpecular = u_MaterialParameters.z; + float materialSpecularPower = u_MaterialParameters.w; + + // Normalize varying parameters, because they are linearly interpolated in the vertex shader. + vec3 viewFragmentDirection = normalize(v_ViewPosition); + vec3 viewNormal = normalize(v_ViewNormal); + + // Flip the y-texture coordinate to address the texture from top-left. + vec4 objectColor = texture2D(u_Texture, vec2(v_TexCoord.x, 1.0 - v_TexCoord.y)); + + // Apply color to grayscale image only if the alpha of u_ObjColor is + // greater and equal to 255.0. + if (u_ObjColor.a >= 255.0) { + float intensity = objectColor.r; + objectColor.rgb = u_ObjColor.rgb * intensity / 255.0; + } + + // Apply inverse SRGB gamma to the texture before making lighting calculations. + objectColor.rgb = pow(objectColor.rgb, vec3(kInverseGamma)); + + // Ambient light is unaffected by the light intensity. + float ambient = materialAmbient; + + // Approximate a hemisphere light (not a harsh directional light). + float diffuse = materialDiffuse * + 0.5 * (dot(viewNormal, viewLightDirection) + 1.0); + + // Compute specular light. + vec3 reflectedLightDirection = reflect(viewLightDirection, viewNormal); + float specularStrength = max(0.0, dot(viewFragmentDirection, reflectedLightDirection)); + float specular = materialSpecular * + pow(specularStrength, materialSpecularPower); + + vec3 color = objectColor.rgb * (ambient + diffuse) + specular; + // Apply SRGB gamma before writing the fragment color. + color.rgb = pow(color, vec3(kGamma)); + // Apply average pixel intensity and color shift + color *= colorShift * (averagePixelIntensity / kMiddleGrayGamma); + gl_FragColor.rgb = color; + gl_FragColor.a = objectColor.a; +} diff --git a/app/src/main/assets/shaders/object.vert b/app/src/main/assets/shaders/object.vert new file mode 100644 index 0000000..2e1e6a4 --- /dev/null +++ b/app/src/main/assets/shaders/object.vert @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +uniform mat4 u_ModelView; +uniform mat4 u_ModelViewProjection; + +attribute vec4 a_Position; +attribute vec3 a_Normal; +attribute vec2 a_TexCoord; + +varying vec3 v_ViewPosition; +varying vec3 v_ViewNormal; +varying vec2 v_TexCoord; + +void main() { + v_ViewPosition = (u_ModelView * a_Position).xyz; + v_ViewNormal = normalize((u_ModelView * vec4(a_Normal, 0.0)).xyz); + v_TexCoord = a_TexCoord; + gl_Position = u_ModelViewProjection * a_Position; +} diff --git a/app/src/main/assets/shaders/plane.frag b/app/src/main/assets/shaders/plane.frag new file mode 100644 index 0000000..d0a4708 --- /dev/null +++ b/app/src/main/assets/shaders/plane.frag @@ -0,0 +1,31 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +precision highp float; +uniform sampler2D u_Texture; +uniform vec4 u_dotColor; +uniform vec4 u_lineColor; +uniform vec4 u_gridControl; // dotThreshold, lineThreshold, lineFadeShrink, occlusionShrink +varying vec3 v_TexCoordAlpha; + +void main() { + vec4 control = texture2D(u_Texture, v_TexCoordAlpha.xy); + float dotScale = v_TexCoordAlpha.z; + float lineFade = max(0.0, u_gridControl.z * v_TexCoordAlpha.z - (u_gridControl.z - 1.0)); + vec3 color = (control.r * dotScale > u_gridControl.x) ? u_dotColor.rgb + : (control.g > u_gridControl.y) ? u_lineColor.rgb * lineFade + : (u_lineColor.rgb * 0.25 * lineFade) ; + gl_FragColor = vec4(color, v_TexCoordAlpha.z * u_gridControl.w); +} diff --git a/app/src/main/assets/shaders/plane.vert b/app/src/main/assets/shaders/plane.vert new file mode 100644 index 0000000..9ac5a6d --- /dev/null +++ b/app/src/main/assets/shaders/plane.vert @@ -0,0 +1,40 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +uniform mat4 u_Model; +uniform mat4 u_ModelViewProjection; +uniform mat2 u_PlaneUvMatrix; +uniform vec3 u_Normal; + +attribute vec3 a_XZPositionAlpha; // (x, z, alpha) + +varying vec3 v_TexCoordAlpha; + +void main() { + vec4 local_pos = vec4(a_XZPositionAlpha.x, 0.0, a_XZPositionAlpha.y, 1.0); + vec4 world_pos = u_Model * local_pos; + + // Construct two vectors that are orthogonal to the normal. + // This arbitrary choice is not co-linear with either horizontal + // or vertical plane normals. + const vec3 arbitrary = vec3(1.0, 1.0, 0.0); + vec3 vec_u = normalize(cross(u_Normal, arbitrary)); + vec3 vec_v = normalize(cross(u_Normal, vec_u)); + + // Project vertices in world frame onto vec_u and vec_v. + vec2 uv = vec2(dot(world_pos.xyz, vec_u), dot(world_pos.xyz, vec_v)); + v_TexCoordAlpha = vec3(u_PlaneUvMatrix * uv, a_XZPositionAlpha.z); + gl_Position = u_ModelViewProjection * local_pos; +} diff --git a/app/src/main/assets/shaders/point_cloud.frag b/app/src/main/assets/shaders/point_cloud.frag new file mode 100644 index 0000000..463d052 --- /dev/null +++ b/app/src/main/assets/shaders/point_cloud.frag @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +precision mediump float; +varying vec4 v_Color; + +void main() { + gl_FragColor = v_Color; +} diff --git a/app/src/main/assets/shaders/point_cloud.vert b/app/src/main/assets/shaders/point_cloud.vert new file mode 100644 index 0000000..627fc1a --- /dev/null +++ b/app/src/main/assets/shaders/point_cloud.vert @@ -0,0 +1,28 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +uniform mat4 u_ModelViewProjection; +uniform vec4 u_Color; +uniform float u_PointSize; + +attribute vec4 a_Position; + +varying vec4 v_Color; + +void main() { + v_Color = u_Color; + gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0); + gl_PointSize = u_PointSize; +} diff --git a/app/src/main/assets/shaders/screenquad.frag b/app/src/main/assets/shaders/screenquad.frag new file mode 100644 index 0000000..800d723 --- /dev/null +++ b/app/src/main/assets/shaders/screenquad.frag @@ -0,0 +1,24 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ +#extension GL_OES_EGL_image_external : require + +precision mediump float; +varying vec2 v_TexCoord; +uniform samplerExternalOES sTexture; + + +void main() { + gl_FragColor = texture2D(sTexture, v_TexCoord); +} diff --git a/app/src/main/assets/shaders/screenquad.vert b/app/src/main/assets/shaders/screenquad.vert new file mode 100644 index 0000000..01c93e3 --- /dev/null +++ b/app/src/main/assets/shaders/screenquad.vert @@ -0,0 +1,24 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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. + */ + +attribute vec4 a_Position; +attribute vec2 a_TexCoord; + +varying vec2 v_TexCoord; + +void main() { + gl_Position = a_Position; + v_TexCoord = a_TexCoord; +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/AStar.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/AStar.java new file mode 100644 index 0000000..e443432 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/AStar.java @@ -0,0 +1,94 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; + +/** + * A*クラス。 + */ +public class AStar { + private LinkedList locationOpenList = new LinkedList(); + private LinkedList locationClosedList = new LinkedList(); + + /** + * 最短経路探査。 最短経路のリストを返すか、見つからない場合nullを返す。 + * start = goalの場合もnullを返す。 + * 戻り値にはgoalからstartの1個前まで経路をさかのぼったものが入っている。startは入っていない。 + */ + public Plan getPath(Location startLocation, + Location goalLocation) { + + //出発地点と目的地点が同じならnullを返す + if (startLocation.equals(goalLocation)) { + return null; + } + + // 初期化 + locationOpenList = new LinkedList(); + locationClosedList = new LinkedList(); + + // 出発地点をLocationOpenListに追加する + locationOpenList.add(startLocation); + while (!locationOpenList.isEmpty()) { + // LocationOpenListの最小スコアの地点をcurLocationとして取り出す + Collections.sort(locationOpenList, new CostComparator()); + Location curLocation = locationOpenList.removeFirst(); + + // 現在の地点が目的地点ならば探査完了 + if (curLocation.equals(goalLocation)) { + LinkedList locationPath = new LinkedList(); + + // 出発地点まで通った地点を辿る + curLocation = goalLocation; + while (!curLocation.equals(startLocation)) { + locationPath.add(curLocation); + curLocation = (Location) curLocation.getParent(); + } + return new Plan(locationPath); + } + + // 現在の地点が目的地点でないなら、現在の地点をLocationClosedListに移す + locationClosedList.add(curLocation); + + // 隣接する地点を調べる + ArrayList neighbors = curLocation.getSuccessors(); + + // 隣接する地点の数だけ、ループ + for (int i = 0; i < neighbors.size(); i++) { + // 通過でき、各リストに入ってないならコストをもらい、 + if (!locationOpenList.contains(neighbors.get(i)) + && !locationClosedList.contains(neighbors.get(i))) { + //地点コストを求める + //agent.getLocationCost((Location)neighbors.get(i), null, null, null); + + //パスコストを求める + ((Location)neighbors.get(i)).calculatesCosts(curLocation, goalLocation); + + // LocationOpenListに移す + locationOpenList.add((Location) neighbors.get(i)); + } + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + + } + return null; + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + // コストの昇順ソートのためのクラスを作ろう + + /** + * ソート条件式クラス。 + */ + class CostComparator implements Comparator { + public int compare(Location n1, Location n2) { + return n1.getScore() - n2.getScore(); + } + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/GeometryGraph.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/GeometryGraph.java new file mode 100644 index 0000000..5cd446b --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/GeometryGraph.java @@ -0,0 +1,168 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.Geometry; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleArray; +import com.google.ar.core.examples.java.common.java3d.Point3d; + +import java.util.ArrayList; + +public class GeometryGraph extends StateMachine { + // コンストラクタ + public GeometryGraph(Geometry g) { + if (g instanceof IndexedTriangleArray) { + + IndexedTriangleArray ita = (IndexedTriangleArray) g; + + // itaからstatesにデータを書き込み + for (int i = 0; i < ita.getIndexCount() / 3; i++) {// 面の数だけループ + Location e = new Location(i, ita); + if (e.getNormal() != null) { + states.add(e); + } + } + + // 関連付け + for (int i = 0; i < states.size(); i++) { + setSuccessors(i, ita); + } + } + } + + + // 連結成分を関連付け + public void setSuccessors(int index, IndexedTriangleArray ita) { + Location e = (Location) states.get(index); + + // 探索 + int[] List = new int[ita.getIndexCount()]; + + for (int i = 0; i < ita.getIndexCount(); i++) { + List[i] = ita.getCoordinateIndex(i); + } + + // -1で初期化 + for (int i = 0; i < 3; i++) { + e.successorIndex[i] = -1; + e.IndexOfSharedEdge[i] = -1; + } + + int j = 0; + + for (int i = 0; i < states.size() * 3; i++) { + // System.out.println("indexList[0] とList["+i+"] の照合"); + // //////idでListを置き換えてメソッド呼び出しを少なく? + // int id = ita.getCoordinateIndex(i); + if (e.indexList[0] == List[i]) { + if (i % 3 == 0) { + if (e.indexList[1] == List[i + 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i + 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 1) { + if (e.indexList[1] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i + 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 2) { + if (e.indexList[1] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i - 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } + + } + } + + for (int i = 0; i < states.size() * 3; i++) { + // System.out.println("indexList[1] とList["+i+"] の照合"); + if (e.indexList[1] == List[i]) { + if (i % 3 == 0) { + if (e.indexList[2] == List[i + 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 1) { + if (e.indexList[2] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 2) { + if (e.indexList[2] == List[i - 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } + } + } + + // System.out.println("1つ目のsuccessor**"+successorIndex[0]+"***"); + // System.out.println("2つ目のsuccessor**"+successorIndex[1]+"***"); + // System.out.println("3つ目のsuccessor**"+successorIndex[2]+"***"); + + return; + } + + + // アクセッサ + public ArrayList getStates() { + return states; + } + + public Location getNearestLocation(Position3D pos) { + Location nearest = null; + double distance = 0.0; + for (int n = 0; n < states.size(); n++) { + Location loc = (Location) states.get(n); + if (nearest == null) { + nearest = loc; + distance = nearest.getCenter().distance( + new Point3d(pos.getVector3d())); + } else { + double d = loc.getCenter().distance( + new Point3d(pos.getVector3d())); + if (d < distance) { + nearest = loc; + distance = d; + } + } + } + return nearest; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/IState.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/IState.java new file mode 100644 index 0000000..e542345 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/IState.java @@ -0,0 +1,16 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import java.util.ArrayList; + +public interface IState { + + abstract ArrayList getSuccessors(); + + abstract void addSuccessor(IState s); + /** + * 探査元のノード取得。 + * @return + * 探査元のノードを返す。 + */ + abstract IState getParent(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Location.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Location.java new file mode 100644 index 0000000..840a6d9 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Location.java @@ -0,0 +1,137 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import com.google.ar.core.examples.java.common.framework.model3D.GeometryUtility; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleArray; +import com.google.ar.core.examples.java.common.java3d.Point3d; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +public class Location implements IState { + public int planeIndex; + public int[] indexList = new int[3]; + public int[] successorIndex = new int[100]; + public int numberOfSharedEdge;// 共有辺の本数 + public int[] IndexOfSharedEdge = new int[3];// 共有辺の頂点インデックス + private Point3d center; + private Vector3d normal; + + private double cost = 0; + private double heuristicCost = 0; + private Location parentNode = null; + private ArrayList successors = new ArrayList(); + + // テスト用の空のコンストラクタ + public Location(Point3d center) { + this.center = center; + normal = new Vector3d(0.0, 1.0, 0.0); + } + + // コンストラクタ + public Location(int index, IndexedTriangleArray ita) { + planeIndex = index; + + indexList[0] = ita.getCoordinateIndex(index * 3); + indexList[1] = ita.getCoordinateIndex(index * 3 + 1); + indexList[2] = ita.getCoordinateIndex(index * 3 + 2); + + Point3d p1 = new Point3d(); + Point3d p2 = new Point3d(); + Point3d p3 = new Point3d(); + + ita.getCoordinate(indexList[0], p1); + ita.getCoordinate(indexList[1], p2); + ita.getCoordinate(indexList[2], p3); + + // 中心座標の計算 + center = new Point3d((p1.getX() + p2.getX() + p3.getX()) / 3.0, (p1 + .getY() + + p2.getY() + p3.getY()) / 3.0, (p1.getZ() + p2.getZ() + p3 + .getZ()) / 3.0); + + // 法線ベクトルの計算 + p2.sub(p1); + p3.sub(p1); + Vector3d v2 = new Vector3d(p2); + Vector3d v3 = new Vector3d(p3); + v2.cross(v2, v3); + if (v2.length() < GeometryUtility.TOLERANCE) { + v2 = null; + } else { + v2.normalize(); + } + normal = v2; + } + + public void addSuccessor(IState s) { + successors.add(s); + } + + @Override + public ArrayList getSuccessors() { + // TODO Auto-generated method stub + return (ArrayList) successors; + } + + @Override + public IState getParent() { + // TODO Auto-generated method stub + return parentNode; + } + + /** + * スコアの取得。 + * + * @return スコアを返す。 + */ + public int getScore() { + // 比較に小数点も反映させる為、1000を掛けている + return (int) ((cost + heuristicCost) * 1000); + } + + /** + * コスト計算と探査元ノードを設定。 + * + * @param parentNode + * 探査元のノード。 + * @param goalNode + * 目的地のノード。 + */ + public void calculatesCosts(Location parentNode, Location goalNode) { + // コストを加算 + cost = parentNode.cost + 1; // agent.getPathCost(parentNode, this, null, + // null, null, null, null, null); + + // //ヒューリスティックコストを計算 + // disX = point.x - goalNode.point.x; + // disY = point.y - goalNode.point.y; + // ヒューリスティックコストの信頼性が薄い為、3分の1にしている + heuristicCost = 0; + + // 探査元ノードを記録 + this.parentNode = parentNode; + } + + // ///////// + // アクセッサ// + // ///////// + public int getPlaneIndex() { + return planeIndex; + } + + public int getIndexList(int index) { + return indexList[index]; + } + + public int getSuccessorIndex(int index) { + return successorIndex[index]; + } + + public Point3d getCenter() { + return center; + } + + public Vector3d getNormal() { + return normal; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Plan.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Plan.java new file mode 100644 index 0000000..ab5e265 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/Plan.java @@ -0,0 +1,73 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.LinkedList; + +public class Plan { + private LinkedList path; // 計画内容を表すパス + private int currentLoc = 0; // パス上の現在の Location + + /** + * 複数の通過点を持つ計画 + * @param locationPath + */ + public Plan(LinkedList locationPath) { + path = locationPath; + currentLoc = locationPath.size() - 1; + } + + /** + * スタートとゴールをダイレクトに結ぶ計画 + * @param start + * @param goal + */ + public Plan(Location start, Location goal) { + path = new LinkedList(); + path.add(goal); + path.add(start); + currentLoc = 1; + } + + /** + * 現在の Location を取得する + * @return 現在の Location, すでにゴールに着いているときは null を返す + */ + public Location getCurrentLocation() { + if (currentLoc <= 0) return null; + return path.get(currentLoc); + } + + /** + * 次の Location を取得する + * @return 次の Location, すでにゴールに着いているときは null を返す + */ + public Location getNextLocation() { + if (currentLoc <= 0) return null; + return path.get(currentLoc - 1); + } + + /** + * 現在の座標値を元に、現在の Location を更新する + * @param position 現在の座標値 + * @return 更新した --- true, 以前のまま --- false + */ + public boolean updateCurrentLocation(Position3D position) { + Vector3d toCurrentPosition = position.getVector3d(); + Location curLocation = getCurrentLocation(); + if (curLocation == null) return true; + toCurrentPosition.sub(curLocation.getCenter()); + double distanceToCurrentPosition = toCurrentPosition.length(); + Vector3d toNextLocation = new Vector3d(getNextLocation().getCenter()); + toNextLocation.sub(curLocation.getCenter()); + double distanceToNextLocation = toNextLocation.length(); + if (distanceToCurrentPosition >= distanceToNextLocation) { + // 次の Location を通り過ぎた場合 + currentLoc--; + return true; + } + return false; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/StateMachine.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/StateMachine.java new file mode 100644 index 0000000..ca7aa66 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/AI/StateMachine.java @@ -0,0 +1,9 @@ +package com.google.ar.core.examples.java.common.framework.AI; + +import java.util.ArrayList; + +public abstract class StateMachine { + protected ArrayList states = new ArrayList(); + protected IState curState; + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTRenderer.java new file mode 100644 index 0000000..da0bad2 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTRenderer.java @@ -0,0 +1,67 @@ +package com.google.ar.core.examples.java.common.framework.RWT; + +import android.opengl.GLSurfaceView.Renderer; + +import com.google.ar.core.examples.java.common.framework.view3D.Camera3D; +import com.google.ar.core.examples.java.common.framework.view3D.Viewer3D; +import com.google.ar.core.examples.java.common.java3d.GraphicsContext3D; +import com.google.ar.core.examples.java.common.java3d.Light; + +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +public class RWTRenderer implements Renderer { + protected Viewer3D viewer; + protected Camera3D camera; + protected GraphicsContext3D gc3D = null; + + public RWTRenderer() { + super(); + } + + public void attachCamera(Camera3D camera) { + // onSurfaceCreated()より先に呼ばれる + this.camera = camera; + viewer = new Viewer3D(camera); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + gc3D = new GraphicsContext3D(gl); + ArrayList lights = camera.getUniverse().getLights(); + for (int i = 0; i < lights.size(); i++){ + Light l = lights.get(i); + gc3D.setLight(l,i); + } + viewer.setGraphicsContext3D(gc3D); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + if (gc3D == null) { + gc3D = new GraphicsContext3D(gl); + viewer.setGraphicsContext3D(gc3D); + } else { + viewer.setGraphicsContext3D(gc3D.setGL10(gl)); + } + viewer.surfaceChanged(width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + if (gc3D == null) { + gc3D = new GraphicsContext3D(gl); + viewer.setGraphicsContext3D(gc3D); + } else { + viewer.setGraphicsContext3D(gc3D.setGL10(gl)); + } + viewer.onDrawFrame(); + + // 3Dモデルのレンダリング + gc3D.pushMatrix(); + camera.getUniverse().render(viewer); + gc3D.popMatrix(); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTSurfaceView.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTSurfaceView.java new file mode 100644 index 0000000..3a03681 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/RWT/RWTSurfaceView.java @@ -0,0 +1,32 @@ +package com.google.ar.core.examples.java.common.framework.RWT; + +import android.content.Context; +import android.opengl.GLSurfaceView; + +public class RWTSurfaceView extends GLSurfaceView { +// protected RWTRenderer renderer; + protected Renderer renderer; + + public RWTSurfaceView(Context context) { + this(context, true); + } + + public RWTSurfaceView(Context context, boolean bInitRenderer) { + super(context); + if (bInitRenderer) { + +// renderer = new RWTRenderer(); + if (context instanceof Renderer) { + renderer = (Renderer) context; + } + + setEGLConfigChooser(8, 8, 8, 8, 16, 0); + this.setRenderer(renderer); + } + } + +// public void attachCamera(Camera3D camera) { +// renderer.attachCamera(camera); +// } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/Animation3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/Animation3D.java new file mode 100644 index 0000000..9c4004a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/Animation3D.java @@ -0,0 +1,137 @@ +package com.google.ar.core.examples.java.common.framework.animation; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D; + +import java.util.ArrayList; + +/** + * 階層化されたオブジェクトに対するアニメーション情報を保持する(部品単位で位置、向き、テクスチャをアニメーション可能) + * @author 新田直也 + * + */ +public class Animation3D { + public long time = 0; + + private ArrayList partList = new ArrayList(); + private long maxKey = getMaxKey(); + + public Animation3D() { + time = 0; + } + + public Animation3D(Animation3D a) { + time = 0; + partList = a.partList; + maxKey = a.maxKey; + } + + public boolean equals(Animation3D a) { + return (partList == a.partList && maxKey == a.maxKey); + } + + public void reset() { + time = 0; + } + + public boolean progress(long interval) { + if (maxKey == 0) + return true; // 空のアニメーションの場合動かさない + time += interval; + // System.out.println(time + "/" + maxKey); + if (time > maxKey) { + time = time % maxKey; // timeが最後の要素のkeyの値(アニメーションの最後のkey)を超えている場合、tを最後のkeyの値(maxKey)で割った余りとして設定する + return false; + } else + return true; + } + + public Pose3D getPose() { + if (maxKey == 0 || partList.size() == 0) + return new DefaultPose3D(); + + KeyFrame[] aroundKey = new KeyFrame[2]; + Quaternion3D q; + Position3D p; + Quaternion3D tq; + Position3D tp; + Pose3D pose = new Pose3D(); + for (int i = 0; i < partList.size(); i++) { + aroundKey = partList.get(i).getKey(time); + + // コピーコンストラクタの作成 + q = null; + p = null; + tq = null; + tp = null; + if (aroundKey[0].getPosition() != null) { + p = new Position3D(aroundKey[0].getPosition()); // getPosition()が参照を返すのでコピーしてから変更 + } + if (aroundKey[0].getQuaternion() != null) { + q = new Quaternion3D(aroundKey[0].getQuaternion()); // getQuaternion()がアドレス(参照)を返すので、コピー(=q)を作成してそちらを変更している。 + } + if (aroundKey[0].getTexturePosition() != null) { + tp = new Position3D(aroundKey[0].getTexturePosition()); // getPosition()が参照を返すのでコピーしてから変更 + } + if (aroundKey[0].getTextureQuaternion() != null) { + tq = new Quaternion3D(aroundKey[0].getTextureQuaternion()); // getQuaternion()がアドレス(参照)を返すので、コピー(=q)を作成してそちらを変更している。 + } + + // timeがkeyそのものだった場合(補間の計算が不要な場合) + if (aroundKey[1] != null) { + // t1 はtの前のkey(aroundKey[0])のスカラー倍 + double t1 = aroundKey[1].key - time; + // t2 はtの後のkey(aroundKey[1])のスカラー倍 + double t2 = time - aroundKey[0].key; + double t3 = aroundKey[1].key - aroundKey[0].key; + double timealpha = t2 / t3; + + // timeに対するQuaternionとPositionの計算 + if (p != null) { + Position3D p2 = new Position3D(aroundKey[1].getPosition()); + p.mul(t1 / t3).add(p2.mul(t2 / t3)); + } + if (q != null) { + Quaternion3D q2 = new Quaternion3D(aroundKey[1].getQuaternion()); + q.getInterpolate(q2, timealpha); + } + if (tp != null) { + Position3D tp2 = new Position3D(aroundKey[1].getTexturePosition()); + tp.mul(t1 / t3).add(tp2.mul(t2 / t3)); + } + if (tq != null) { + Quaternion3D tq2 = new Quaternion3D(aroundKey[1].getTextureQuaternion()); + tq.getInterpolate(tq2, timealpha); + } + } + pose.addPose(partList.get(i).getName(), p, q, aroundKey[0].getTexture(), tp, tq, partList.get(i).getTextureUnit()); + } + + return pose; + } + + public Animation3D merge(Animation3D a) { + this.partList.addAll(a.partList); + maxKey = getMaxKey(); + return this; + } + + public void addPartAnimation(PartAnimation pa) { + partList.add(pa); + maxKey = getMaxKey(); + } + + // アニメーションが終了したかを判定するためにkeyの最大値を探索して返すメソッド + private long getMaxKey() { + long maxKey = 0; + int i; + + for (i = 0; i < partList.size(); i++) { + if (maxKey < partList.get(i).getLastKey()) { + maxKey = partList.get(i).getLastKey(); + } else + continue; + } + return maxKey; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/DefaultPose3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/DefaultPose3D.java new file mode 100644 index 0000000..07d2953 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/DefaultPose3D.java @@ -0,0 +1,27 @@ +package com.google.ar.core.examples.java.common.framework.animation; + +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.java3d.Transform3D; + +/** + * 初期状態の姿勢 + * @author 新田直也 + * + */ +public class DefaultPose3D extends Pose3D { + public void applyTo(Object3D obj) { + obj.scale.setTransform(new Transform3D()); + for (int n = 0; n < obj.children.length; n++) { + subApplyTo((Object3D)obj.children[n]); + } + } + + private void subApplyTo(Object3D obj) { + obj.pos.setTransform(new Transform3D()); + obj.rot.setTransform(new Transform3D()); + obj.scale.setTransform(new Transform3D()); + for (int n = 0; n < obj.children.length; n++) { + subApplyTo((Object3D)obj.children[n]); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/KeyFrame.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/KeyFrame.java new file mode 100644 index 0000000..cb96b17 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/KeyFrame.java @@ -0,0 +1,41 @@ +package com.google.ar.core.examples.java.common.framework.animation; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D; +import com.google.ar.core.examples.java.common.java3d.Texture; + + +/** + * アニメーション中でキーフレーム単位に定義される情報(キー値) + * @author 新田直也 + * + */ +public class KeyFrame { + long key = 0; + Position3D keyValuePosition = null; + Quaternion3D keyValueQuaternion = null; + Texture keyValueTexture = null; + Position3D keyValueTexturePosition = null; + Quaternion3D keyValueTextureQuaternion = null; + + Position3D getPosition() { + return keyValuePosition; + } + + Quaternion3D getQuaternion() { + return keyValueQuaternion; + } + + Texture getTexture() { + return keyValueTexture; + } + + Position3D getTexturePosition() { + return keyValueTexturePosition; + } + + Quaternion3D getTextureQuaternion() { + return keyValueTextureQuaternion; + } +} + diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/PartAnimation.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/PartAnimation.java new file mode 100644 index 0000000..75ef524 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/animation/PartAnimation.java @@ -0,0 +1,176 @@ +package com.google.ar.core.examples.java.common.framework.animation; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D; + +import java.util.ArrayList; + + +/** + * 部品単位のアニメーション情報を保持する + * @author 新田直也 + * + */ +public class PartAnimation { + private String name; + private int textureUnit; + private ArrayList keyList = new ArrayList(); + public static final int SINGLE_TEXTURE = -1; + + public PartAnimation(String name) { + this.name = name; + textureUnit = SINGLE_TEXTURE; + } + + public PartAnimation(String name, int textureUnit) { + this.name = name; + this.textureUnit = textureUnit; + } + + String getName() { + return name; + } + + int getTextureUnit() { + return textureUnit; + } + + //アニメーションの経過時間の前後のkeyをKeyFrame型の配列で取得するメソッド + KeyFrame[] getKey(long t){ + int i; + KeyFrame[] aroundKey = new KeyFrame[2]; + + for(i=1;i partPoseList = new ArrayList(); + + public Pose3D() { + } + + public Pose3D(Pose3D p) { + partPoseList = new ArrayList(p.partPoseList); + } + + public void applyTo(Object3D obj) { + // obj.rotate(part, vx, vy, vz, a); + int n = partPoseList.size(); + for (int i = 0; i < n; i++) { + final PartPose partpose = (PartPose) partPoseList.get(i); + Object3D partObj = obj.getPart(partpose.name); + if (partObj != null) { + // 位置 + if (partpose.position != null) + partObj.apply(partpose.position, false); + // 向き + if (partpose.quaternion != null) + partObj.apply(partpose.quaternion, false); + // テクスチャ + if (partpose.texture != null + || partpose.texturePosition != null + || partpose.textureQuaternion != null) { + ObjectVisitor v = new ObjectVisitor() { + @Override + public void postVisit(Object3D obj) { + Appearance appearance = null; + Node node = obj.getPrimitiveNode(); + if (node != null && node instanceof Shape3D) { + appearance = ((Shape3D)node).getAppearance(); + } else if (node != null && node instanceof Primitive) { + appearance = ((Primitive)node).getAppearance(); + } + if (appearance != null) { + TextureUnitState tu = null; + if (partpose.texture != null) { + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + appearance.setTexture(partpose.texture); + } else { + tu = appearance.getTextureUnitState(partpose.textureUnit); + tu.setTexture(partpose.texture); + appearance.setTextureUnitState(partpose.textureUnit, tu); + } + } + if ((partpose.texturePosition != null || partpose.textureQuaternion != null) + && obj.hasAppearancePrepared()) { // Appearance が初回のレンダリングの直前で更新される可能性があるため + TextureAttributes ta; + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + ta = appearance.getTextureAttributes(); + } else { + tu = appearance.getTextureUnitState(partpose.textureUnit); + ta = tu.getTextureAttributes(); + } + if (ta == null) { + ta = new TextureAttributes(); + } + Transform3D t = new Transform3D(); + if (partpose.texturePosition != null) { + t.set(new Vector3d(partpose.texturePosition.getVector3d())); + } + if (partpose.textureQuaternion != null) { + Transform3D t2 = new Transform3D(); + t2.set(partpose.textureQuaternion.getQuat()); + t.mul(t2); + } +// shape.getAppearance().getTextureAttributes().setTextureTransform(t); + ta.setTextureTransform(t); + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + appearance.setTextureAttributes(ta); + } else { + tu.setTextureAttributes(ta); + appearance.setTextureUnitState(partpose.textureUnit, tu); + } + } + } + } + @Override + public void preVisit(Object3D obj) { + } + }; + partObj.accept(v); + } + } + } + } + + public void addPose(String name, Position3D p, Quaternion3D q, Texture texture, Position3D tp, Quaternion3D tq, int textureUnit) { + PartPose partpose = new PartPose(name, p, q, texture, tp, tq, textureUnit); + partPoseList.add(partpose); + } + + @Override + public Property3D clone() { + // TODO Auto-generated method stub + return new Pose3D(this); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Actor.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Actor.java new file mode 100644 index 0000000..94c2d7a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Actor.java @@ -0,0 +1,349 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + + +import com.google.ar.core.examples.java.common.framework.animation.Animation3D; +import com.google.ar.core.examples.java.common.framework.model3D.CollisionResult; +import com.google.ar.core.examples.java.common.framework.model3D.GeometryUtility; +import com.google.ar.core.examples.java.common.framework.model3D.Movable; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D; +import com.google.ar.core.examples.java.common.framework.physics.Force3D; +import com.google.ar.core.examples.java.common.framework.physics.Ground; +import com.google.ar.core.examples.java.common.framework.physics.PhysicsUtility; +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; +import com.google.ar.core.examples.java.common.framework.physics.Velocity3D; +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + + +/** + * ゲーム内の登場物全般 + * @author 新田直也 + * + */ +public abstract class Actor extends Animatable implements Movable { + protected Vector3d direction = new Vector3d(1.0, 0.0, 0.0); + protected Mode mode; + // 以下省メモリ化のため予めインスタンスを生成 + protected Mode modeFreeFall = new ModeFreeFall(); + protected Mode modeOnGround = new ModeOnGround(); + + public Actor(Object3D body, Animation3D animation) { + super(new Solid3D(body), animation); + mode = modeOnGround; + } + + public Actor(Solid3D body, Animation3D animation) { + super(body, animation); + mode = modeOnGround; + } + +// public Actor(ActorModel model) { +// super(model.createBody(), model.getAnimation()); +// mode = modeOnGround; +// } + + /** + * 単位時間ごとの動作(衝突判定処理は行わない) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + */ + public void motion(long interval){ + // 1. 位置を動かす + ((Solid3D)body).move(interval, getGravity(), getGravityCenter()); + super.motion(interval); + } + + /** + * 単位時間ごとの動作(衝突判定処理も行う) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + * @param ground --- 地面(構造物) + */ + public void motion(long interval, Ground ground) { + + // 1. 位置を動かす + ((Solid3D)body).move(interval, getGravity(), getGravityCenter()); + + if (animation != null) { + // 2. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 3. 姿勢を変える + body.apply(animation.getPose(), false); + } + + // 4. 衝突判定 + CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground); + + // 5. 衝突応答 + if (cr != null) { + // 構造物にぶつかった、または接触している時 + if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) { + // 上向きの面(=地面)にぶつかった、または接触している時 + if (cr.length <= GeometryUtility.TOLERANCE) { + // 地面に乗っている + if (!(mode instanceof ModeOnGround)) { + // 落ちてきて丁度乗った + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } + } else { + // 地面にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + if (!(mode instanceof ModeOnGround)) { + // 落ちてきてめり込んだ + // 6. 地面モードにする + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } else { + // 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ + // または、歩いている途中で斜面に差し掛かった + ((ModeOnGround)mode).setNormal(cr.normal); + } + } + } else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) { + // 垂直壁にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + } else { + // 下からぶつかった、または接触した(頭をぶつけた) + if (cr.length > GeometryUtility.TOLERANCE) { + // 5.1. 押し戻す + onIntersect(cr, interval); + } + } + cr = null; + } else { + // 地面とぶつかっても接触してもいない時 + // 6. 落下モードにする + mode = modeFreeFall; + } + } + + public void motion(long interval, Ground ground, ArrayList forces, + ArrayList appPoints) { + + forces.add(getGravity()); + appPoints.add(getGravityCenter()); + + // 1. 位置を動かす + ((Solid3D)body).move(interval, forces, appPoints); + + if (animation != null) { + // 2. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 3. 姿勢を変える + body.apply(animation.getPose(), false); + } + + // 4. 衝突判定 + CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground); + + // 5. 衝突応答 + if (cr != null) { + // 構造物にぶつかった、または接触している時 + if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) { + // 上向きの面(=地面)にぶつかった、または接触している時 + if (cr.length <= GeometryUtility.TOLERANCE) { + // 地面に乗っている + if (!(mode instanceof ModeOnGround)) { + // 落ちてきて丁度乗った + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } + } else { + // 地面にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + if (!(mode instanceof ModeOnGround)) { + // 落ちてきてめり込んだ + // 6. 地面モードにする + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } else { + // 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ + // または、歩いている途中で斜面に差し掛かった + ((ModeOnGround)mode).setNormal(cr.normal); + } + } + } else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) { + // 垂直壁にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + } else { + // 下からぶつかった、または接触した(頭をぶつけた) + if (cr.length > GeometryUtility.TOLERANCE) { + // 5.1. 押し戻す + onIntersect(cr, interval); + } + } + cr = null; + } else { + // 地面とぶつかっても接触してもいない時 + // 6. 落下モードにする + mode = modeFreeFall; + } + } + + public void setInitialDirection(Vector3d dir) { + direction = dir; + } + + public Vector3d getInitialDirection() { + return direction; + } + + /** + * 指定した方向に向かせる + * @param vec 新しい向き + */ + public void setDirection(Vector3d vec) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(direction, GeometryUtility.Y_AXIS); + v2.cross(vec, GeometryUtility.Y_AXIS); + if (v2.length() < GeometryUtility.TOLERANCE) return; + v1.normalize(); + v2.normalize(); + double cos = v1.dot(v2); + v1.cross(v1, v2); + double sin = v1.dot(GeometryUtility.Y_AXIS); + double angle = Math.atan2(sin, cos); + AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle); + Quaternion3D quat = new Quaternion3D(axisAngle); + ((Solid3D)body).apply(quat, false); + } + + /** + * 指定した方向に向かせる + * @param vec 新しい向き + */ + public void setDirection3D(Vector3d vec) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(direction, GeometryUtility.Y_AXIS); + double angle = Math.PI / 2.0 - Math.acos(vec.dot(GeometryUtility.Y_AXIS)); + AxisAngle4d axisAngle2 = new AxisAngle4d(v1, angle); + v2.cross(vec, GeometryUtility.Y_AXIS); + if (v2.length() < GeometryUtility.TOLERANCE) return; + v1.normalize(); + v2.normalize(); + double cos = v1.dot(v2); + v1.cross(v1, v2); + double sin = v1.dot(GeometryUtility.Y_AXIS); + angle = Math.atan2(sin, cos); + AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle); + Quaternion3D quat = new Quaternion3D(axisAngle); + Quaternion3D quat2 = new Quaternion3D(axisAngle2); + quat.mul(quat2); + ((Solid3D)body).apply(quat, false); + } + + /** + * 現在向いている方向を取得する + * @return 現在の向き + */ + public Vector3d getDirection() { + Vector3d dir = new Vector3d(direction); + Transform3D trans = new Transform3D(); + trans.set(((Solid3D)body).getQuaternion().getAxisAngle()); + trans.transform(dir); + return dir; + } + + /** + * 移動速度ベクトルを設定する + * @param vel 新しい移動速度ベクトル + */ + public void setVelocity(Velocity3D vel) { + ((Solid3D)body).apply(vel, false); + } + + /** + * 移動速度ベクトルを取得する + * @return 現在の移動速度ベクトル + */ + public Velocity3D getVelocity() { + return ((Solid3D)body).getVelocity(); + } + + /** + * X軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotX(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.X_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * Y軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotY(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.Y_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * Z軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotZ(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.Z_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * 加わっている重力を取得する + * @return 重力 + */ + public Force3D getGravity() { + return mode.getForce((Solid3D)body); + } + + /** + * 重心を取得する + * @return 重心位置 + */ + public Position3D getGravityCenter() { + return ((Solid3D)body).getGravityCenter(); + } + + /** + * 地面に乗っている状態か否かを取得する + * @return true --- 地面に乗っている, false --- 地面に乗っていない(空中にいる) + */ + public boolean isOnGround() { + return (mode instanceof ModeOnGround); + } + + /** + * 地面(構造物)に落下した瞬間に呼び出される + */ + public abstract void onEndFall(); + + /** + * 地面(構造物)に衝突した瞬間に呼び出される + * @param normal --- 地面の法線方向ベクトル + * @param interval --- 前回の動作からの経過時間(ミリ秒単位) + */ + public abstract void onIntersect(CollisionResult normal, long interval); + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ActorModel.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ActorModel.java new file mode 100644 index 0000000..8ef8786 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ActorModel.java @@ -0,0 +1,17 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + +import android.content.res.Resources; + +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; + + +public abstract class ActorModel extends GameModel { + + public ActorModel(Resources res, String fileName) { + super(res, fileName); + } + + public Solid3D createBody() { + return new Solid3D(getModel().createObject()); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Animatable.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Animatable.java new file mode 100644 index 0000000..df6a7f0 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Animatable.java @@ -0,0 +1,57 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + + +import com.google.ar.core.examples.java.common.framework.animation.Animation3D; +import com.google.ar.core.examples.java.common.framework.model3D.BaseObject3D; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Placeable; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; + +public abstract class Animatable implements Placeable { + public Object3D body; + public Animation3D animation; + + public Animatable(Object3D body, Animation3D animation) { + this.body = body; + this.animation = animation; + } + + /** + * 単位時間ごとの動作(アニメーション処理) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + */ + public void motion(long interval) { + if (animation != null) { + // 1. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 2. 姿勢を変える + body.apply(animation.getPose(), false); + } + } + + public TransformGroup getTransformGroupToPlace() { + return getBody().getTransformGroupToPlace(); + } + + public BaseObject3D getBody() { + return body; + } + + /** + * アニメーションが終了するたびに呼ばれる + */ + public abstract void onEndAnimation(); + + public Position3D getPosition() { + return body.getPosition3D(); + } + + public void setPosition(Position3D p) { + body.apply(p, false); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/GameModel.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/GameModel.java new file mode 100644 index 0000000..f95b4e6 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/GameModel.java @@ -0,0 +1,43 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + +import android.content.res.Resources; + +import com.google.ar.core.examples.java.common.framework.animation.Animation3D; +import com.google.ar.core.examples.java.common.framework.model3D.Model3D; +import com.google.ar.core.examples.java.common.framework.model3D.ModelFactory; +import com.google.ar.core.examples.java.common.framework.model3D.ModelFileFormatException; + +import java.io.IOException; + +public class GameModel { + + private Model3D model = null; + private Resources res; + private String fileName; + + public GameModel(Resources res, String fileName) { + this.res = res; + this.fileName = fileName; + } + + public Animation3D getAnimation() { + return new Animation3D(); + } + + public Model3D getModel() { + if(model == null && fileName != null) { + try { + model = ModelFactory.loadModel(res, fileName); + } catch (IOException e) { + e.printStackTrace(); + } catch (ModelFileFormatException e) { + e.printStackTrace(); + } + } + return model; + } + + public void clearModel(){ + model = null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Mode.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Mode.java new file mode 100644 index 0000000..03808af --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/Mode.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; +import com.google.ar.core.examples.java.common.framework.physics.Force3D; +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; + + +public abstract class Mode { + + abstract Force3D getForce(Solid3D body); + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeFreeFall.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeFreeFall.java new file mode 100644 index 0000000..d0e7cbf --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeFreeFall.java @@ -0,0 +1,11 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; +import com.google.ar.core.examples.java.common.framework.physics.Force3D; +import com.google.ar.core.examples.java.common.framework.physics.PhysicsUtility; +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; + + +public class ModeFreeFall extends Mode { + Force3D getForce(Solid3D body) { + return PhysicsUtility.getGravity(body); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeOnGround.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeOnGround.java new file mode 100644 index 0000000..da4cbc3 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/ModeOnGround.java @@ -0,0 +1,24 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + + +import com.google.ar.core.examples.java.common.framework.physics.Force3D; +import com.google.ar.core.examples.java.common.framework.physics.PhysicsUtility; +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class ModeOnGround extends Mode { + private Vector3d normal = PhysicsUtility.vertical; + + public Force3D getForce(Solid3D body) { + return Force3D.ZERO; + } + + public void setNormal(Vector3d normal) { + this.normal = normal; + } + + public Vector3d getNormal() { + return normal; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/OvergroundActor.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/OvergroundActor.java new file mode 100644 index 0000000..ae6605b --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/OvergroundActor.java @@ -0,0 +1,51 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + + +import com.google.ar.core.examples.java.common.framework.animation.Animation3D; +import com.google.ar.core.examples.java.common.framework.model3D.CollisionResult; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.physics.Solid3D; +import com.google.ar.core.examples.java.common.framework.physics.Velocity3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +/** + * 地面の上を移動するもの(ジャンプや自由落下させることも可能) + * @author 新田直也 + * + */ +public class OvergroundActor extends Actor { + + public OvergroundActor(Object3D body, Animation3D animation) { + super(body, animation); + } + + public OvergroundActor(Solid3D body, Animation3D animation) { + super(body, animation); + } + +// public OvergroundActor(ActorModel model) { +// super(model); +// } + + public void onIntersect(CollisionResult cr, long interval) { + // めり込んだら(めり込んだ面の法線方向に)押し戻す + Vector3d back = (Vector3d) cr.normal.clone(); + back.scale(cr.length * 2.0); + body.apply(new Position3D(body.getPosition3D().add(back)), false); + + // 速度の面の法線方向の成分を 0 にする + Vector3d v = (Vector3d) ((Solid3D)body).getVelocity().getVector3d().clone(); + double d = v.dot(cr.normal); + v.scaleAdd(-d, cr.normal, v); + body.apply(new Velocity3D(v), false); + } + + @Override + public void onEndFall() { + } + + @Override + public void onEndAnimation() { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTime3DActivity.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTime3DActivity.java new file mode 100644 index 0000000..87537ad --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTime3DActivity.java @@ -0,0 +1,35 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + +import android.os.Bundle; + +import com.google.ar.core.examples.java.common.framework.RWT.RWTSurfaceView; +import com.google.ar.core.examples.java.common.framework.model3D.Universe; +import com.google.ar.core.examples.java.common.framework.view3D.Camera3D; + +public abstract class RealTime3DActivity extends RealTimeActivity { + protected Universe universe; + protected Camera3D camera; + protected RWTSurfaceView view; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + universe = new Universe(); + + camera = new Camera3D(universe); + + view = new RWTSurfaceView(this); + view.setRenderMode(RWTSurfaceView.RENDERMODE_WHEN_DIRTY); +// view.attachCamera(camera); + setContentView(view); + } + + protected void update(long interval) { + progress(interval); + universe.update(interval); + camera.adjust(interval); + view.requestRender(); + } + + abstract protected void progress(long interval); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTimeActivity.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTimeActivity.java new file mode 100644 index 0000000..6b81467 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/gameMain/RealTimeActivity.java @@ -0,0 +1,61 @@ +package com.google.ar.core.examples.java.common.framework.gameMain; + +import android.app.Activity; +import android.os.Bundle; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public abstract class RealTimeActivity extends Activity implements Runnable { + //インターバル確認用変数 + private long interval = 15L; + private long prevTime = 0L; + + private ScheduledThreadPoolExecutor schedule = new ScheduledThreadPoolExecutor(1); + private boolean fixedInterval; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + protected void start(long interval){ + this.interval = interval; + this.fixedInterval = false; + schedule.scheduleWithFixedDelay(this, interval, interval, TimeUnit.MILLISECONDS); + } + + protected void start(long delay, long interval){ + this.interval = interval; + this.fixedInterval = false; + schedule.scheduleWithFixedDelay(this, delay, interval, TimeUnit.MILLISECONDS); + } + + protected void start(long delay, long interval, boolean fixedInterval){ + this.interval = interval; + this.fixedInterval = fixedInterval; + schedule.scheduleWithFixedDelay(this, delay, interval, TimeUnit.MILLISECONDS); + } + + protected void stop() { + schedule.shutdown(); + } + + //繰り返し実行される部分 + public void run(){ + long interval; + if (prevTime == 0L || fixedInterval) { + interval = this.interval; + prevTime = System.currentTimeMillis(); + } else { + long curTime = System.currentTimeMillis(); + interval = curTime - prevTime; + prevTime = curTime; + } + update(interval); + } + + //intervalミリ秒のインターバルをおいて定期実行 + protected abstract void update(long interval); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BackgroundBox.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BackgroundBox.java new file mode 100644 index 0000000..54643e6 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BackgroundBox.java @@ -0,0 +1,6 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Background; + +public class BackgroundBox extends Background { +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BaseObject3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BaseObject3D.java new file mode 100644 index 0000000..ed36364 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BaseObject3D.java @@ -0,0 +1,331 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Appearance; +import com.google.ar.core.examples.java.common.java3d.BoundingPolytope; +import com.google.ar.core.examples.java.common.java3d.Box; +import com.google.ar.core.examples.java.common.java3d.Cone; +import com.google.ar.core.examples.java.common.java3d.Cylinder; +import com.google.ar.core.examples.java.common.java3d.Geometry; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleArray; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleFanArray; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleStripArray; +import com.google.ar.core.examples.java.common.java3d.Node; +import com.google.ar.core.examples.java.common.java3d.Shape3D; +import com.google.ar.core.examples.java.common.java3d.Sphere; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; +import com.google.ar.core.examples.java.common.java3d.TriangleArray; +import com.google.ar.core.examples.java.common.java3d.TriangleFanArray; +import com.google.ar.core.examples.java.common.java3d.TriangleStripArray; +import com.google.ar.core.examples.java.common.java3d.Vector3d; +import com.google.ar.core.examples.java.common.java3d.Vector4d; + +import java.util.ArrayList; +import java.util.Enumeration; + +public class BaseObject3D implements Placeable { + public TransformGroup center; + + protected BoundingSurface[] boundingSurfaces = null; + + public BaseObject3D() { + center = new TransformGroup(); + } + + public BaseObject3D(Geometry g, Appearance a) { + center = new TransformGroup(); + Shape3D shape = new Shape3D(g, a); + center.addChild(shape); + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return getBody().center; + } + + public BaseObject3D getBody() { + return this; + } + + public Node getPrimitiveNode() { + return (Node)center.getChild(0); + } + + public Enumeration getPrimitiveNodes() { + return (Enumeration)center.getAllChildren(); + } + + /** + * 衝突判定用のボリューム(ポリゴンと粗い判定用の多角柱)を取得する + * @return 衝突判定用のボリューム列 + */ + public BoundingSurface[] getBoundingSurfaces() { + if (boundingSurfaces == null) { + Node node = getPrimitiveNode(); + if (node == null) return null; + + ArrayList vertex3DList = null; + if (node instanceof Box) { + // Boxの場合 + Box box = ((Box)node); + // 頂点列を取得する + vertex3DList = getVertexList(box.getShape(Box.BACK).getGeometry()); + vertex3DList.addAll(getVertexList(box.getShape(Box.BOTTOM).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.FRONT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.LEFT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.RIGHT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.TOP).getGeometry())); + } else if (node instanceof Cylinder) { + // Cylinderの場合 + Cylinder cylinder = ((Cylinder)node); + // 頂点列を取得する + vertex3DList = getVertexList(cylinder.getShape(Cylinder.BODY).getGeometry()); + vertex3DList.addAll(getVertexList(cylinder.getShape(Cylinder.BOTTOM).getGeometry())); + vertex3DList.addAll(getVertexList(cylinder.getShape(Cylinder.TOP).getGeometry())); + } else if (node instanceof Cone) { + // Coneの場合 + Cone cone = ((Cone)node); + // 頂点列を取得する + vertex3DList = getVertexList(cone.getShape(Cone.BODY).getGeometry()); + vertex3DList.addAll(getVertexList(cone.getShape(Cone.CAP).getGeometry())); + } else if (node instanceof Sphere) { + // Sphereの場合 + Sphere sphere = ((Sphere)node); + // 頂点列を取得する + vertex3DList = getVertexList(sphere.getShape(Sphere.BODY).getGeometry()); + } else if (node instanceof Shape3D) { + // Shape3Dの場合 + Shape3D shape = (Shape3D)node; + // 頂点列を取得する + vertex3DList = getVertexList(shape.getGeometry()); + } + if (vertex3DList == null) return null; + + BoundingSurface[] surfaces = new BoundingSurface[vertex3DList.size() / 3]; + + for (int i = 0; i < vertex3DList.size(); i += 3) { + Vector3d v1 = vertex3DList.get(i); + Vector3d v2 = vertex3DList.get(i + 1); + Vector3d v3 = vertex3DList.get(i + 2); + BoundingSurface bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v1.clone()); + bSurface.addVertex((Vector3d)v2.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); + surfaces[i / 3] = bSurface; + } + boundingSurfaces = surfaces; + } + return boundingSurfaces; + } + + private ArrayList getVertexList(Geometry g) { + ArrayList vertex3DList = new ArrayList(); + double coordinate1[] = new double[3]; + if (g instanceof IndexedTriangleArray) { + // IndexedTriangleArray の場合 + IndexedTriangleArray triArray = (IndexedTriangleArray)g; + + // 全頂点を3D上の頂点をvertex3DListに入れていく。 + for (int i = 0; i < triArray.getIndexCount(); i++) { + triArray.getCoordinates(triArray.getCoordinateIndex(i), coordinate1); + vertex3DList.add(new Vector3d(coordinate1)); + } + } else if (g instanceof TriangleArray) { + // TriangleArray の場合 + TriangleArray triArray = (TriangleArray)g; + + // 全頂点を3D上の頂点をvertex3DListに入れていく。 + for (int i = 0; i < triArray.getVertexCount(); i++) { + triArray.getCoordinates(i, coordinate1); + vertex3DList.add(new Vector3d(coordinate1)); + } + } else if (g instanceof IndexedTriangleStripArray) { + // IndexedTriangleStripArray の場合 + IndexedTriangleStripArray triStripAttay = (IndexedTriangleStripArray)g; + int stripVertexCounts[] = new int[triStripAttay.getNumStrips()]; + triStripAttay.getStripIndexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + for (int i = 0; i < triStripAttay.getNumStrips(); i++) { + for (int j = 0; j < stripVertexCounts[i]; j += 2) { + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); + vertex3DList.add(new Vector3d(coordinate1)); //1つめの三角形 + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + vertex3DList.add(new Vector3d(coordinate2)); //2つめの三角形 + vertex3DList.add(new Vector3d(coordinate4)); + vertex3DList.add(new Vector3d(coordinate3)); + index += 2; + } + } + } else if (g instanceof TriangleStripArray) { + // TriangleStripArray の場合 + TriangleStripArray triStripAttay = (TriangleStripArray)g; + int stripVertexCounts[] = new int[triStripAttay.getNumStrips()]; + triStripAttay.getStripVertexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + for (int i = 0; i < triStripAttay.getNumStrips(); i++) { + for (int j = 0; j < stripVertexCounts[i]; j += 2) { + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index+1, coordinate2); + triStripAttay.getCoordinates(index+2, coordinate3); + triStripAttay.getCoordinates(index+3, coordinate4); + vertex3DList.add(new Vector3d(coordinate1)); //1つめの三角形 + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + vertex3DList.add(new Vector3d(coordinate2)); //2つめの三角形 + vertex3DList.add(new Vector3d(coordinate4)); + vertex3DList.add(new Vector3d(coordinate3)); + index += 2; + } + } + } else if (g instanceof IndexedTriangleFanArray) { + // IndexedTriangleFanArray の場合 + IndexedTriangleFanArray triFanAttay = (IndexedTriangleFanArray)g; + int stripVertexCounts[] = new int[triFanAttay.getNumStrips()]; + triFanAttay.getStripIndexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = null; + for (int i = 0; i < triFanAttay.getNumStrips(); i++) { + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index), coordinate1); // 中心点 + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index+1), coordinate2); + index += 2; + for (int j = 2; j < stripVertexCounts[i]; j++) { + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index), coordinate3); + vertex3DList.add(new Vector3d(coordinate1)); + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + coordinate4 = coordinate2; + coordinate2 = coordinate3; + coordinate3 = coordinate4; + index++; + } + } + } else if (g instanceof TriangleFanArray) { + // TriangleFanArray の場合 + TriangleFanArray triFanAttay = (TriangleFanArray)g; + int stripVertexCounts[] = new int[triFanAttay.getNumStrips()]; + triFanAttay.getStripVertexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = null; + for (int i = 0; i < triFanAttay.getNumStrips(); i++) { + triFanAttay.getCoordinates(index, coordinate1); // 中心点 + triFanAttay.getCoordinates(index + 1, coordinate2); + index += 2; + for (int j = 2; j < stripVertexCounts[i]; j++) { + triFanAttay.getCoordinates(index, coordinate3); + vertex3DList.add(new Vector3d(coordinate1)); + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + coordinate4 = coordinate2; + coordinate2 = coordinate3; + coordinate3 = coordinate4; + index++; + } + } + } else { + return null; + } + return vertex3DList; + } + + protected BoundingPolytope createBoundingPolytope(Vector3d vertex1, + Vector3d vertex2, Vector3d vertex3) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + Vector3d v3 = new Vector3d(); + Vector3d v4 = new Vector3d(); + Vector3d v5 = new Vector3d(); + Vector3d v6 = new Vector3d(); + Vector3d cv1 = new Vector3d(); + Vector3d cv2 = new Vector3d(); + cv1.sub(vertex3, vertex1); + cv2.sub(vertex2, vertex1); + Vector3d cv = new Vector3d(); + cv.cross(cv1, cv2); + cv.normalize(); + cv.scale(0.01); + v1.set(vertex1); + v2.set(vertex2); + v3.set(vertex3); + v4.set(vertex1); + v4.add(cv); + v5.set(vertex2); + v5.add(cv); + v6.set(vertex3); + v6.add(cv); + + Vector3d pv1 = new Vector3d(); + Vector3d pv2 = new Vector3d(); + Vector3d pv3 = new Vector3d(); + Vector3d pn = new Vector3d(); + Vector4d[] plane = new Vector4d[5]; + + // 0 + pv1 = v1; + pv2.sub(v2, v1); + pv3.sub(v3, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[0] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 1 + pv1 = v1; + pv2.sub(v4, v1); + pv3.sub(v2, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[1] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 2 + pv1 = v1; + pv2.sub(v3, v1); + pv3.sub(v4, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[2] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 3 + pv1 = v6; + pv2.sub(v3, v6); + pv3.sub(v5, v6); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[3] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 4 + pv1 = v6; + pv2.sub(v5, v6); + pv3.sub(v4, v6); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[4] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + return new BoundingPolytope(plane); + } + + public boolean hasAppearancePrepared() { + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BoundingSurface.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BoundingSurface.java new file mode 100644 index 0000000..47cb9ab --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/BoundingSurface.java @@ -0,0 +1,128 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.BoundingPolytope; +import com.google.ar.core.examples.java.common.java3d.BoundingSphere; +import com.google.ar.core.examples.java.common.java3d.Bounds; +import com.google.ar.core.examples.java.common.java3d.Matrix4d; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; +import com.google.ar.core.examples.java.common.java3d.Vector4d; + +import java.util.ArrayList; + + +public class BoundingSurface implements Cloneable { + private Bounds bounds = null; // 粗い衝突判定用(粗い地面用) + private ArrayList children = new ArrayList(); + private ArrayList vertexList = new ArrayList(); + private static Vector4d plane[] = { new Vector4d(), new Vector4d(), new Vector4d(), + new Vector4d(), new Vector4d() }; + + public Object clone() { + BoundingSurface s = new BoundingSurface(); + if (bounds != null) { + s.setBounds((Bounds)bounds.clone()); + } + for (int i = 0; i < children.size(); i++) { + s.children.add((BoundingSurface)children.get(i).clone()); + } + for (int i = 0; i < vertexList.size(); i++) { + s.vertexList.add((Vector3d)vertexList.get(i).clone()); + } + return s; + } + + public void setBounds(Bounds bounds) { + this.bounds = bounds; + } + + public Bounds getBounds() { + return bounds; + } + + public void addVertex(Vector3d v) { + vertexList.add(v); + } + + public void addChild(BoundingSurface bs, boolean bCombineBounds) { + children.add(bs); + if (bCombineBounds) { + if (bounds == null) { + bounds = (Bounds)bs.bounds.clone(); + } else { + bounds.combine(bs.bounds); + } + } + } + + public void transform(Transform3D transform3D) { + bounds.transform(transform3D); + for (int i = 0; i < vertexList.size(); i++) { + Matrix4d mat4d = new Matrix4d(); + transform3D.get(mat4d); + double x = mat4d.m00 * vertexList.get(i).x + mat4d.m01 + * vertexList.get(i).y + mat4d.m02 * vertexList.get(i).z + + mat4d.m03; + double y = mat4d.m10 * vertexList.get(i).x + mat4d.m11 + * vertexList.get(i).y + mat4d.m12 * vertexList.get(i).z + + mat4d.m13; + double z = mat4d.m20 * vertexList.get(i).x + mat4d.m21 + * vertexList.get(i).y + mat4d.m22 * vertexList.get(i).z + + mat4d.m23; + vertexList.get(i).x = x; + vertexList.get(i).y = y; + vertexList.get(i).z = z; + } + } + + /** + * BoundingSphereとの粗い衝突判定 + * @param bs 衝突判定の対象 + * @return 衝突しているBoundingSurfaceのリスト(nullを返すことはない) + */ + public ArrayList intersect(BoundingSphere bs) { + ArrayList results = new ArrayList(); + if (children == null || children.size() == 0) { + if (bounds.intersect(bs)) { + results.add(this); + } + } else { + if (bounds == null || bounds.intersect(bs)) { + for (int n = 0; n < children.size(); n++) { + results.addAll(children.get(n).intersect(bs)); + } + } + } + return results; + } + + /** + * OBBとの詳細な衝突判定 + * @param obb 衝突判定の対象 + * @return 衝突判定の結果 + */ + public CollisionResult intersect(OBB obb) { + if (children == null || children.size() == 0) { + // 葉の場合は、凸ポリゴンを立ち上げた薄い多角柱 + if (bounds instanceof BoundingPolytope) { + ((BoundingPolytope)bounds).getPlanes(plane); + CollisionResult cr = obb.intersect(plane[0]); // obbが底面を含む無限平面と交わっているか? + if (cr != null) { + // 無限平面と交わっている場合、無限平面との衝突点が凸ポリゴンの内部に位置するか? + if (GeometryUtility.inside(vertexList, cr.collisionPoint.getVector3d(), cr.normal)) { + return cr; + } + } + } + return null; + } else { + for (int n = 0; n < children.size(); n++) { + CollisionResult cr = children.get(n).intersect(obb); + if (cr != null) { + return cr; + } + } + return null; + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/CollisionResult.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/CollisionResult.java new file mode 100644 index 0000000..bca55fb --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/CollisionResult.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class CollisionResult { + public Position3D collisionPoint = new Position3D(); + public Vector3d normal = new Vector3d(); + public double length = 0.0; +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ContainerModel.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ContainerModel.java new file mode 100644 index 0000000..2aa96cc --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ContainerModel.java @@ -0,0 +1,42 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Transform3D; + +/** + * 子供を持つモデル + * @author 新田直也 + * + */ +public class ContainerModel extends Model3D { + private Model3D[] children; + private Transform3D defaultTransform = null; + + public ContainerModel(String name, Model3D[] children) { + this.children = children; + this.name = name; + this.defaultTransform = null; + } + + public ContainerModel(String name, Model3D[] children, + Transform3D transform) { + this.children = children; + this.name = name; + this.defaultTransform = transform; + } + + public Object3D createObject() { + Object3D[] objChild = new Object3D[children.length]; + for(int i = 0;i < children.length;i++){ + objChild[i] = (Object3D) children[i].createObject(); + } + return new Object3D(name, objChild, defaultTransform); + } + + public Object3D createObjectSharingAppearance() { + Object3D[] objChild = new Object3D[children.length]; + for(int i = 0;i < children.length;i++){ + objChild[i] = (Object3D) children[i].createObjectSharingAppearance(); + } + return new Object3D(name, objChild, defaultTransform); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryCollector.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryCollector.java new file mode 100644 index 0000000..81c47fc --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryCollector.java @@ -0,0 +1,99 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Geometry; +import com.google.ar.core.examples.java.common.java3d.IndexedGeometryArray; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleArray; +import com.google.ar.core.examples.java.common.java3d.Shape3D; + +import java.util.ArrayList; + +public class GeometryCollector extends ObjectVisitor{ + private int totalVertexCount = 0; + private int totalIndexCount = 0; + private ArrayList geometryList = new ArrayList(); // 葉のとき、Geometryを保存 + private IndexedGeometryArray geometry = null; + + @Override + public void preVisit(Object3D obj) { + if (!obj.hasChildren()) { + Geometry g = ((Shape3D)obj.getBody().getPrimitiveNode()).getGeometry(); + geometryList.add(g); + totalVertexCount += ((IndexedGeometryArray)g).getVertexCount(); + totalIndexCount += ((IndexedGeometryArray)g).getIndexCount(); + } + } + + @Override + public void postVisit(Object3D obj) { + } + + public Geometry getGeometry(){ + if (geometry == null) { + // リスト中の複数の Geometry を1つにまとめる + int coordinateOfs = 0; + int indexOfs = 0; + double coodinates[] = new double[totalVertexCount * 3]; + int indicies[] = new int[totalIndexCount]; + for (int n = 0; n < geometryList.size(); n++) { + IndexedGeometryArray geometry = (IndexedGeometryArray)geometryList.get(n); + double tmpCoordinates[] = new double[geometry.getVertexCount() * 3]; + geometry.getCoordinates(0, tmpCoordinates); + System.arraycopy(tmpCoordinates, 0, coodinates, coordinateOfs * 3, geometry.getVertexCount() * 3); + int tmpIndicies[] = new int[geometry.getIndexCount()]; + geometry.getCoordinateIndices(0, tmpIndicies); + for (int m = 0; m < geometry.getIndexCount(); m++) { + tmpIndicies[m] += coordinateOfs; + } + System.arraycopy(tmpIndicies, 0, indicies, indexOfs, geometry.getIndexCount()); + coordinateOfs += geometry.getVertexCount(); + indexOfs += geometry.getIndexCount(); + } + geometry = new IndexedTriangleArray(totalVertexCount, IndexedTriangleArray.COORDINATES, totalIndexCount); + geometry.setCoordinates(0, coodinates); + geometry.setCoordinateIndices(0, indicies); + + // 重複している頂点を1つにまとめる + GeometryUtility.compressGeometry(geometry); + +// // IndexedTriangleStripArray に変換(重複している頂点を1つにまとめるため) +// GeometryInfo gi = new GeometryInfo(geometry); +// Stripifier sf = new Stripifier(); +// sf.stripify(gi); +// geometry = gi.getIndexedGeometryArray(); // IndexedTriangleStripArray が返される +// +// // IndexedTriangleArray に変換(GeometryGraph が IndexedTriangleStripArray に未対応のため) +// if (geometry instanceof IndexedTriangleStripArray) { +// int numStrips = ((IndexedTriangleStripArray)geometry).getNumStrips(); +// int indexCounts[] = new int[numStrips]; +// ((IndexedTriangleStripArray)geometry).getStripIndexCounts(indexCounts); +// int dstIndicies[] = new int[(((IndexedTriangleStripArray)geometry).getIndexCount() - numStrips * 2) * 3]; +// int dstOfs = 0; +// int srcOfs = 0; +// for (int s = 0; s < numStrips; s++) { +// for (int i = 2; i < indexCounts[s]; i += 2) { +// dstIndicies[dstOfs] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 2); +// dstIndicies[dstOfs + 1] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 1); +// dstIndicies[dstOfs + 2] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i); +// dstOfs += 3; +// if (i + 1 < indexCounts[s]) { +// dstIndicies[dstOfs] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i); +// dstIndicies[dstOfs + 1] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 1); +// dstIndicies[dstOfs + 2] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i + 1); +// dstOfs += 3; +// } +// } +// srcOfs += indexCounts[s]; +// } +// +// int vertexCount = ((IndexedTriangleStripArray)geometry).getVertexCount(); +// coodinates = new double[vertexCount * 3]; +// ((IndexedTriangleStripArray)geometry).getCoordinates(0, coodinates); +// +// geometry = new IndexedTriangleArray(vertexCount, IndexedTriangleArray.COORDINATES, dstIndicies.length); +// geometry.setCoordinates(0, coodinates); +// geometry.setCoordinateIndices(0, dstIndicies); +// } + } + return geometry; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryUtility.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryUtility.java new file mode 100644 index 0000000..322ef25 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/GeometryUtility.java @@ -0,0 +1,266 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.IndexedGeometryArray; +import com.google.ar.core.examples.java.common.java3d.Matrix3d; +import com.google.ar.core.examples.java.common.java3d.Point3d; +import com.google.ar.core.examples.java.common.java3d.Vector3d; +import com.google.ar.core.examples.java.common.java3d.Vector4d; + +import java.util.ArrayList; +import java.util.Hashtable; + +public class GeometryUtility { + public static final double TOLERANCE = 0.0000002; + public static final Vector3d X_AXIS = new Vector3d(1.0, 0.0, 0.0); + public static final Vector3d Y_AXIS = new Vector3d(0.0, 1.0, 0.0); + public static final Vector3d Z_AXIS = new Vector3d(0.0, 0.0, 1.0); + + public static ProjectionResult projection3D( + ArrayList vertex3Dlist, Vector3d axis) { + double k = 0.0; + int i = 0; + + ProjectionResult pr = new ProjectionResult(); + + axis.normalize(); + + for (i = 0; i < vertex3Dlist.size(); i++) { + Vector3d p = vertex3Dlist.get(i); + Vector3d p1 = new Vector3d(); + + k = p.dot(axis); + p1.scaleAdd(-k, axis, p); + + if (i == 0 || k >= pr.max) { + pr.max = k; + } else if (i == 0 || k <= pr.min) { + pr.min = k; + } + pr.vertexList.add(p1); + } + return pr; + } + + public static ProjectionResult projection2D( + ArrayList vertex2Dlist, Vector3d axis) { + int i = 0; + double k = 0.0; + // System.out.println("point3:"+axis); + if (axis.x != 0 || axis.y != 0 || axis.z != 0) { + axis.normalize(); + } + // System.out.println("point3_1:"+axis); + ProjectionResult pr = new ProjectionResult(); + + for (i = 0; i < vertex2Dlist.size(); i++) { + Vector3d p = vertex2Dlist.get(i); + + k = p.dot(axis); + // System.out.println("k:"+k); + // System.out.println("point3:"+axis); + if (i == 0 || k >= pr.max) { + pr.max = k; + } else if (i == 0 || k <= pr.min) { + pr.min = k; + } + } + return pr; + } + + // 以下、頂点の表裏の判定メソッド + public static boolean inside(Vector3d v, Vector4d plane) { + // System.out.println("vertex:" + v.x + "," + v.y + "," + v.z); + // System.out.println("plane:" + plane.x + "," + plane.y + "," + plane.z + // + "," + plane.w); + Vector3d pv = new Vector3d(plane.x, plane.y, plane.z); + // Vector3d nv = (Vector3d)pv.clone(); + // nv.scaleAdd(plane.w / pv.lengthSquared(),v); + if (pv.dot(v) + plane.w <= TOLERANCE) { + // System.out.println("内部判定!!"+nv.dot(pv)); + pv = null; + return true; + } + // System.out.println("外部判定!!"+nv.dot(pv)); + pv = null; + return false; + } + + /** + * 3点を通る平面を作成する(ただし、v1, v2, v3の内容が書き換えられるので注意) + * + * @param v1 + * --- 1点目の座標 + * @param v2 + * --- 2点目の座標 + * @param v3 + * --- 3点目の座標 + * @return v1, v2, v3を通る平面 + */ + public static Vector4d createPlane(Vector3d v1, Vector3d v2, Vector3d v3) { + v2.sub(v1); + v3.sub(v1); + v2.cross(v2, v3); + v2.normalize(); + return new Vector4d(v2.x, v2.y, v2.z, -v2.dot(v1)); + } + + /** + * + * 平面と直線の交点を求める + * + * @param plane + * 平面 + * @param v1 + * 直線上の点1 + * @param v2 + * 直線状の点2 + * @return 交点 + */ + public static Vector3d intersect(Vector4d plane, Vector3d v1, Vector3d v2) { + Vector3d n = new Vector3d(plane.x, plane.y, plane.z); + Vector3d v21 = (Vector3d) v2.clone(); + v21.sub(v1); + v21.scale((-plane.w - n.dot(v1)) / n.dot(v21)); + v21.add(v1); + return v21; + } + + /** + * + * 指定された点が凸ポリゴンの内部に包含されているか? + * + * @param vertexList + * 凸ポリゴンの頂点列 + * @param point + * 指定点 + * @return true --- 包含されている, false --- 包含されていない + */ + public static boolean inside(ArrayList vertexList, Vector3d point, Vector3d normal) { + boolean inside = true; + for (int i = 0; i < vertexList.size(); i++) { + // ポリゴンの各辺に対して衝突点が右側か左側か? + Vector3d center = (Vector3d) point.clone(); + Vector3d v2 = (Vector3d) (vertexList.get((i + 1) + % vertexList.size()).clone()); + Vector3d v1 = (Vector3d) vertexList.get(i).clone(); + center.sub(v1); + v2.sub(v1); + v1.cross(v2, center); + if (normal.dot(v1) < -GeometryUtility.TOLERANCE) { + inside = false; + break; + } + } + // すべて右側、またはすべて左側だった場合、凸ポリゴンの内部に位置したと考える + return inside; + } + + // IndexedGeometryArrayのインデックスのうち、同じ座標をさすインデックスを置き換える + public static void compressGeometry(IndexedGeometryArray g) { + // 1. Hashtableを作りながら、同一座標頂点群の代表representaion[]を決める + Hashtable> h = new Hashtable>(); + Point3d p = new Point3d(); + Point3d p2 = new Point3d(); + double hashValue; + ArrayList vertexList; + int[] representation = new int[g.getVertexCount()]; + + for (int i = 0; i < g.getVertexCount(); i++) { + g.getCoordinate(i, p); + + hashValue = p.getX() + p.getY() + p.getZ(); + + vertexList = h.get(hashValue); + + if (vertexList == null) {// hashに対応する要素がない場合 + // Hashtableを作る + vertexList = new ArrayList(); + vertexList.add(i); + h.put(hashValue, vertexList); + // representation[]を作る + representation[i] = i; + } else { + boolean bFound = false; + for (int j = 0; j < vertexList.size(); j++) { + g.getCoordinate(vertexList.get(j), p2); + if (p.getX() == p2.getX() + && p.getY() == p2.getY() + && p.getZ() == p2.getZ()) { + representation[i] = vertexList.get(j); + bFound = true; + break; + } + } + if (!bFound) { + vertexList.add(i); + // representation[]を作る + representation[i] = i; + } + } + } + + // 2. indexの置き換え + for (int i = 0; i < g.getIndexCount(); i++) { + int newIndex = representation[g.getCoordinateIndex(i)]; + g.setCoordinateIndex(i, newIndex); + } + } + + public static Matrix3d calcRotationForView(Vector3d viewLine) { + Vector3d v1 = (Vector3d)viewLine.clone(); + v1.normalize(); + Vector3d v2 = new Vector3d(); + v2.cross(v1, Y_AXIS); + double angle2 = Math.atan2(-v2.z, v2.x); + double angle1 = Math.PI / 2.0 - Math.acos(v1.dot(Y_AXIS)); + Matrix3d rot1 = new Matrix3d(); + rot1.rotX(angle1); + Matrix3d rot2 = new Matrix3d(); + rot2.rotY(angle2); + rot2.mul(rot1); + return rot2; + } + + public static Quaternion3D calcQuaternionForView(Vector3d viewLine) { + Vector3d v1 = (Vector3d)viewLine.clone(); + v1.normalize(); + Vector3d v2 = new Vector3d(); + v2.cross(v1, Y_AXIS); + double angle2 = Math.atan2(-v2.z, v2.x); + double angle1 = Math.PI / 2.0 - Math.acos(v1.dot(Y_AXIS)); + Quaternion3D quat1 = new Quaternion3D(new AxisAngle4d(X_AXIS, angle1)); + Quaternion3D quat2 = new Quaternion3D(new AxisAngle4d(Y_AXIS, angle2)); + + return quat1.mul(quat2); + } + + public static Quaternion3D calcQuaternionFromSrcToDst(Vector3d src, Vector3d dst) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(src, Y_AXIS); + v2.cross(dst, Y_AXIS); + Quaternion3D quat1; + Quaternion3D quat2; + double angle2 = Math.acos(Y_AXIS.dot(src) / src.length()) - Math.acos(Y_AXIS.dot(dst) / dst.length()); + if (Math.abs(v2.length()) < TOLERANCE) { + v1.normalize(); + quat1 = new Quaternion3D(); + quat2 = new Quaternion3D(new AxisAngle4d(v1, angle2)); + } else { + if (Math.abs(v1.length()) < TOLERANCE) { + quat1 = new Quaternion3D(); + } else { + v1.normalize(); + v2.normalize(); + double cos1 = v1.dot(v2); + v1.cross(v1, v2); + double sin1 = v1.dot(Y_AXIS); + double angle1 = Math.atan2(sin1, cos1); + quat1 = new Quaternion3D(new AxisAngle4d(Y_AXIS, angle1)); + } + quat2 = new Quaternion3D(new AxisAngle4d(v2, angle2)); + } + return quat1.mul(quat2); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/IViewer3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/IViewer3D.java new file mode 100644 index 0000000..8c72e99 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/IViewer3D.java @@ -0,0 +1,18 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.GraphicsContext3D; +import com.google.ar.core.examples.java.common.java3d.Light; +import com.google.ar.core.examples.java.common.java3d.Node; +import com.google.ar.core.examples.java.common.java3d.Transform3D; + +import java.util.ArrayList; + +public interface IViewer3D { + public abstract void setGraphicsContext3D(GraphicsContext3D gc3D); + public abstract void surfaceChanged(int width, int height); + public abstract void onDrawFrame(); + public abstract void update(ArrayList lights, BackgroundBox skyBox); + public abstract void pushTransform(Transform3D t); + public abstract void popTransform(); + public abstract void draw(Node obj); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/LeafModel.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/LeafModel.java new file mode 100644 index 0000000..6b58c36 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/LeafModel.java @@ -0,0 +1,27 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Appearance; +import com.google.ar.core.examples.java.common.java3d.Geometry; + +public class LeafModel extends Model3D { + + private Geometry g; + private Appearance a; + + public LeafModel(String name, Geometry g, Appearance a){ + this.name = name; + this.g = g; + this.a = a; + } + + public Object3D createObject(){ + Appearance a2 = (Appearance)a.cloneNodeComponent(); + Object3D obj = new Object3D(name, g, a2); + return obj; + } + + public Object3D createObjectSharingAppearance(){ + Object3D obj = new Object3D(name, g, a); + return obj; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Model3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Model3D.java new file mode 100644 index 0000000..17b816d --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Model3D.java @@ -0,0 +1,9 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + + + +public abstract class Model3D { + String name; + public abstract Object3D createObject(); + public abstract Object3D createObjectSharingAppearance(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFactory.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFactory.java new file mode 100644 index 0000000..6742461 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFactory.java @@ -0,0 +1,896 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import android.content.res.Resources; + +import com.google.ar.core.examples.java.common.java3d.Appearance; +import com.google.ar.core.examples.java.common.java3d.GeometryArray; +import com.google.ar.core.examples.java.common.java3d.IndexedGeometryArray; +import com.google.ar.core.examples.java.common.java3d.IndexedTriangleArray; +import com.google.ar.core.examples.java.common.java3d.Material; +import com.google.ar.core.examples.java.common.java3d.TextureLoader; +import com.google.ar.core.examples.java.common.java3d.TriangleArray; +import com.google.ar.core.examples.java.common.java3d.Vector2f; +import com.google.ar.core.examples.java.common.java3d.Vector3f; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; + + +/** + * 3次元モデルを生成するためのクラス + * @author 新田直也 + * + */ +public class ModelFactory { + public static final String STL_FILE_EXTENSION = ".stl"; + public static final String OBJ_FILE_EXTENSION = ".obj"; + + /** + * 3次元モデルを階層構造と各部の名称とともにファイルから読み込む + * @param res リソース + * @param fileName ファイル名 + * @return 読み込んだ3Dモデル + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(Resources res, String fileName) throws IOException, ModelFileFormatException{ + return loadModel(res, fileName, null, false, false); + } + + /** + * 3次元モデルを階層構造と各部の名称とともにファイルから読み込む + * @param res リソース + * @param fileName ファイル名 + * @param ap 3Dモデルに適用する表面属性(STLファイルのみ) + * @return 読み込んだ3Dモデル + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(Resources res, String fileName, Appearance ap) throws IOException, ModelFileFormatException{ + return loadModel(res, fileName, ap, false, false); + } + + /** + * 3次元モデルを階層構造と各部の名称とともにファイルから読み込む(透明テクスチャの有無の指定付き) + * @param res リソース + * @param fileName ファイル名 + * @param ap 3Dモデルに適用する表面属性(STLファイルのみ) + * @param bTransparent 透明テクスチャがあるか否か(Android用では未使用) + * @param bCalcNormal 法線の再計算を行うか + * @return 読み込んだ3Dモデル + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(Resources res, String fileName, Appearance ap, boolean bTransparent, boolean bCalcNormal) throws IOException, ModelFileFormatException { + if (fileName.toLowerCase().endsWith(STL_FILE_EXTENSION)) { + // STLファイル読み込み + return loadStlFile(res, fileName, ap, bCalcNormal); + } else if (fileName.toLowerCase().endsWith(OBJ_FILE_EXTENSION)) { + // OBJファイル読み込み + return loadObjFile(res, fileName, bCalcNormal); + } + return null; + } + + /** + * STLファイル(アスキー、バイナリ)の読み込み + * @param res リソース + * @param fileName ファイル名 + * @param ap 3Dモデルに適用する表面属性 + * @param bCalcNormal 法線の再計算を行うか + * @return 読み込んだ3Dモデル + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadStlFile(Resources res, String fileName, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // STLファイル読み込み + InputStream in = res.getAssets().open(fileName); + BufferedInputStream bis = new BufferedInputStream(in); + byte[] headerBin = new byte[80]; + int n = bis.read(headerBin); + if (headerBin[0] == 's' && + headerBin[1] == 'o' && + headerBin[2] == 'l' && + headerBin[3] == 'i' && + headerBin[4] == 'd' && + headerBin[5] == ' ') { + bis.close(); + + // STLはテキストファイル + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + Model3D model = loadStlAsciiFile(br, ap, bCalcNormal); + br.close(); + return model; + } else if (n == 80) { + // STLはバイナリファイル + Model3D model = loadStlBinaryFile(bis, headerBin, ap, bCalcNormal); + bis.close(); + return model; + } + return null; + } + + private static Model3D loadStlAsciiFile(BufferedReader br, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // ヘッダ読み込み + String header = br.readLine(); + if (header == null) { + br.close(); + throw new ModelFileFormatException(); + } + header.trim(); + String headerContents[] = header.split(" "); + String name; + if (headerContents.length > 0 && headerContents[0].equals("solid")) { + if (headerContents.length > 1) name = headerContents[1]; + else name = ""; + } else { + System.out.println("STL file's header error!!"); + br.close(); + throw new ModelFileFormatException(); + } + + // Facet読み込み + ArrayList verticies = new ArrayList(); + String line; + while ((line = br.readLine()) != null && !line.startsWith("endsolid ")) { + // facet normal ... + line.trim(); + String[] data = line.split(" "); + if (data.length < 1 || !data[0].equals("facet")) { + System.out.println("Expected facet normal ..."); + br.close(); + throw new ModelFileFormatException(); + } + + // outer loop + line = br.readLine(); + if (line == null) { + System.out.println("Expected outer loop"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("outer loop")) { + System.out.println("Expected outer loop"); + br.close(); + throw new ModelFileFormatException(); + } + + // vertex * 3 + for (int i = 0; i < 3; i++) { + line = br.readLine(); + if (line == null) { + System.out.println("Expected vertex x y z"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + data = line.split(" "); + if (data.length < 4 || !data[0].equals("vertex")) { + System.out.println("Expected vertex x y z"); + br.close(); + throw new ModelFileFormatException(); + } + double[] vertex = new double[]{ + Double.parseDouble(data[1]), // X座標 + Double.parseDouble(data[2]), // Y座標 + Double.parseDouble(data[3]) // Z座標 + }; + verticies.add(vertex); // Z座標 + } + + // endloop + line = br.readLine(); + if (line == null) { + System.out.println("Expected endloop"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("endloop")) { + System.out.println("Expected endloop"); + br.close(); + throw new ModelFileFormatException(); + } + + // endfacet + line = br.readLine(); + if (line == null) { + System.out.println("Expected endfacet"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("endfacet")) { + System.out.println("Expected endfacet"); + br.close(); + throw new ModelFileFormatException(); + } + } + + TriangleArray triArray = new TriangleArray(verticies.size(), TriangleArray.COORDINATES); + for (int n = 0; n < verticies.size(); n++) { + triArray.setCoordinate(n, verticies.get(n)); + } + if (ap == null) ap = new Appearance(); + return new LeafModel(name, triArray, ap); + } + + private static Model3D loadStlBinaryFile(BufferedInputStream bis, byte[] header, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // Facet数読み込み + Integer numTri = readInt(bis); + if (numTri == null) { + System.out.println("Expected vertex count"); + bis.close(); + throw new ModelFileFormatException(); + } + + // Facet読み込み + ArrayList verticies = new ArrayList(); + ArrayList normals = new ArrayList(); + Vector3f[] vertex = new Vector3f[] { + new Vector3f(), + new Vector3f(), + new Vector3f() + }; + + for (int n = 0; n < numTri; n++) { + Float nx = readFloat(bis); // 法線X成分 + if (nx == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + Float ny = readFloat(bis); // 法線Y成分 + if (ny == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + Float nz = readFloat(bis); // 法線Z成分 + if (nz == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + for (int i = 0; i < 3; i++) { + Float x = readFloat(bis); // X座標 + if (x == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + Float y = readFloat(bis); // Y座標 + if (y == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + Float z = readFloat(bis); // Z座標 + if (z == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + verticies.add(new float[]{x, y, z}); + vertex[i].x = x; + vertex[i].y = y; + vertex[i].z = z; + } + if (bCalcNormal) { + vertex[1].sub(vertex[0]); + vertex[2].sub(vertex[0]); + vertex[1].normalize(); + vertex[2].normalize(); + vertex[0].cross(vertex[1], vertex[2]); + nx = vertex[0].x; + ny = vertex[0].y; + nz = vertex[0].z; + } + normals.add(new float[]{nx, ny, nz}); + normals.add(new float[]{nx, ny, nz}); + normals.add(new float[]{nx, ny, nz}); + + byte[] optionBin = new byte[2]; + if (bis.read(optionBin) < 2) { + System.out.println("Expected option"); + bis.close(); + throw new ModelFileFormatException(); + } + } + + TriangleArray triArray = new TriangleArray(verticies.size(), TriangleArray.COORDINATES | TriangleArray.NORMALS); + for (int n = 0; n < verticies.size(); n++) { + triArray.setCoordinate(n, verticies.get(n)); + triArray.setNormal(n, normals.get(n)); + } + + if (ap == null) ap = new Appearance(); + return new LeafModel(new String(header), triArray, ap); + } + + /** + * OBJファイルの読み込み + * @param res リソース + * @param fileName ファイル名 + * @param bCalcNormal 法線の再計算を行うか + * @return 読み込んだ3Dモデル + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadObjFile(Resources res, String fileName, boolean bCalcNormal) throws IOException, ModelFileFormatException { + InputStream in = res.getAssets().open(fileName); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + HashMap appearances = new HashMap(); + ArrayList objects = new ArrayList(); + ArrayList groups = new ArrayList(); + ArrayList subGroups = new ArrayList(); + ArrayList objCoordinates = new ArrayList(); + ArrayList objTexCoordinates = new ArrayList(); + ArrayList objNormals = new ArrayList(); + ArrayList> subGroupCoordinateIndicies = new ArrayList>(); + ArrayList> subGroupTexCoordIndicies = new ArrayList>(); + ArrayList> subGroupNormalIndicies = new ArrayList>(); + String objectName = ""; + String groupName = ""; + String subGroupName = ""; + Appearance subGroupAp = null; + + String line = null; + while ((line = br.readLine()) != null) { + line.trim(); + String data[] = line.split(" "); + if (data[0].equals("#")) { + // コメント + } else if (data[0].equals("mtllib")) { + // 材質ファイルの指定 + String dir = new File(fileName).getParent(); + String mtlFileName = line.substring(line.indexOf(" ") + 1); + String mtlFilePath = new File(dir, mtlFileName).getPath(); + appearances = loadMtlFile(res, mtlFilePath); + } else if (data[0].equals("o")) { + // オブジェクトの開始 + if (groups.size() > 0 || subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 前のオブジェクトのグループの残り、サブグループの残り、ストリップの残りを新しいオブジェクトとして追加 + Model3D newObject = createRemainingObject(objectName, groups, groupName, objCoordinates, + objTexCoordinates, objNormals, subGroups, subGroupName, subGroupAp, + subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + objects.add(newObject); + groups.clear(); + } + + if (data.length < 1) { + System.out.println("Expected object's name"); + throw new ModelFileFormatException(); + } + objectName = data[1]; + } else if (data[0].equals("g")) { + // グループの開始 + if (subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 前のグループのサブグループの残り、ストリップの残りを新しいグループとして追加 + Model3D newGroup = createRemainingGroup(groupName, objCoordinates, objTexCoordinates, objNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + groups.add(newGroup); + subGroups.clear(); + } + + if (data.length < 1) { + System.out.println("Expected group's name"); + throw new ModelFileFormatException(); + } + groupName = data[1]; + } else if (data[0].equals("usemtl")) { + // 材質の使用(サブグループの開始) + if (subGroupCoordinateIndicies.size() > 0) { + // 前のサブグループのストリップの残りを新しいサブグループとして追加 + Model3D newSubGroup = createRemainingSubGroup(objCoordinates, objTexCoordinates, objNormals, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + subGroups.add(newSubGroup); + subGroupCoordinateIndicies.clear(); + subGroupTexCoordIndicies.clear(); + subGroupNormalIndicies.clear(); + } + + if (data.length < 1) { + System.out.println("Expected mtl file's name"); + throw new ModelFileFormatException(); + } + subGroupAp = appearances.get(data[1]); + subGroupName = groupName + "_" + data[1] + subGroups.size(); // ※サブグループの名前が重複しないようにする処理 + } else if (data[0].equals("v")) { + // 頂点座標データ + if (data.length < 4) { + System.out.println("Expected vertex coordinate"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + float z = Float.parseFloat(data[3]); + Vector3f v = new Vector3f(x, y, z); + objCoordinates.add(v); + } else if (data[0].equals("vt")) { + // テクスチャ座標データ + if (data.length < 3) { + System.out.println("Expected texture coordinate"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + Vector2f v = new Vector2f(x, y); + objTexCoordinates.add(v); + } else if (data[0].equals("vn")) { + // 法線ベクトルデータ + if (data.length < 4) { + System.out.println("Expected normal vector"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + float z = Float.parseFloat(data[3]); + Vector3f v = new Vector3f(x, y, z); + objNormals.add(v); + } else if (data[0].equals("f")) { + // ポリゴン(ストリップ)データ + if (data.length < 4) { + System.out.println("At least 3 verticies are needed"); + throw new ModelFileFormatException(); + } + ArrayList coordinateIndicies = new ArrayList(); + ArrayList texCoordIndicies = new ArrayList(); + ArrayList normalIndicies = new ArrayList(); + for (int n = 1; n < data.length; n++) { + String elements[] = data[n].split("/"); + if (elements.length >= 1) { + // 頂点座標インデックス + coordinateIndicies.add(Integer.parseInt(elements[0]) - 1); + if (elements.length >= 2) { + // テクスチャ座標インデックス + if (elements[0].length() > 0) { + // "v//vn"の場合もあるため + texCoordIndicies.add(Integer.parseInt(elements[1]) - 1); + } + if (elements.length >= 3) { + // 法線ベクトルインデックス + normalIndicies.add(Integer.parseInt(elements[2]) - 1); + } + } + } + } + subGroupCoordinateIndicies.add(coordinateIndicies); + subGroupTexCoordIndicies.add(texCoordIndicies); + subGroupNormalIndicies.add(normalIndicies); + } + } + + // ファイル読み込み後の処理 + if (groups.size() > 0 || subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // ストリップの残り、サブグループの残り、グループの残りを最後のオブジェクトとして追加 + Model3D newObject = createRemainingObject(objectName, groups, + groupName, objCoordinates, objTexCoordinates, objNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + objects.add(newObject); + } + br.close(); + + if (objects.size() == 0) { + System.out.println("No polygon is included"); + throw new ModelFileFormatException(); + } else if (objects.size() == 1) { + if (groups.size() == 0) { + System.out.println("No polygon is included"); + throw new ModelFileFormatException(); + } else if (groups.size() == 1) { + // グループがただ一つの場合 + return groups.get(0); + } else { + // オブジェクトがただ一つの場合 + return objects.get(0); + } + } else { + // オブジェクトが複数の場合 + return new ContainerModel(fileName, (Model3D [])objects.toArray(new Model3D []{})); + } + } + + /** + * グループの残りを新しいオブジェクトとして追加 + * @param objectName 新しいオブジェクトの名前 + * @param groups 新しいオブジェクトを構成するグループ + * @param groupName + * @param groupCoordinates + * @param groupTexCoordinates + * @param groupNormals + * @param subGroups + * @param subGroupName + * @param subGroupAp + * @param subGroupCoordinateIndicies + * @param subGroupTexCoordIndicies + * @param subGroupNormalIndicies + * @return + */ + private static Model3D createRemainingObject(String objectName, ArrayList groups, + String groupName, ArrayList groupCoordinates, ArrayList groupTexCoordinates, + ArrayList groupNormals, ArrayList subGroups, + String subGroupName, Appearance subGroupAp, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + if (subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // ストリップの残り、サブグループの残りを新しいグループとして追加 + Model3D newGroup = createRemainingGroup(groupName, groupCoordinates, groupTexCoordinates, groupNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + groups.add(newGroup); + subGroups.clear(); + } + + // グループの残りを新しいオブジェクトとして作成 + Model3D newObject = new ContainerModel(objectName, (Model3D [])groups.toArray(new Model3D []{})); + return newObject; + } + + /** + * サブグループの残りを新しいグループとして作成 + * @param groupName 新しいグループの名前 + * @param groupCoordinates 頂点座標の値のリスト + * @param groupTexCoordinates テクスチャ座標の値のリスト + * @param groupNormals 法線ベクトルの値のリスト + * @param subGroups 新しいグループを構成するサブグループ + * @param subGroupName + * @param subGroupAp + * @param subGroupCoordinateIndicies + * @param subGroupTexCoordIndicies + * @param subGroupNormalIndicies + * @return + */ + private static Model3D createRemainingGroup(String groupName, + ArrayList groupCoordinates, ArrayList groupTexCoordinates, ArrayList groupNormals, + ArrayList subGroups, + String subGroupName, Appearance subGroupAp, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + if (subGroupCoordinateIndicies.size() > 0) { + // ストリップの残りを新しいサブグループとして追加 + Model3D newSubGroup = createRemainingSubGroup(groupCoordinates, groupTexCoordinates, groupNormals, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + subGroups.add(newSubGroup); + subGroupCoordinateIndicies.clear(); + subGroupTexCoordIndicies.clear(); + subGroupNormalIndicies.clear(); + } + + // サブグループの残りを新しいグループとして作成 + return new ContainerModel(groupName, (Model3D [])subGroups.toArray(new Model3D []{})); + } + + /** + * ストリップの残りを新しいサブグループとして作成 + * @param groupCoordinates + * @param groupTexCoordinates + * @param groupNormals + * @param subGroupName 新しいサブグループの名前 + * @param subGroupAp 新しいサブグループの表面属性 + * @param subGroupCoordinateIndicies 頂点座標へのインデックス + * @param subGroupTexCoordIndicies テクスチャ座標へのインデックス + * @param subGroupNormalIndicies 法線ベクトルへのインデックス + * @return + */ + private static Model3D createRemainingSubGroup(ArrayList groupCoordinates, + ArrayList groupTexCoordinates, ArrayList groupNormals, + String subGroupName, Appearance subGroupAp, ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + // ストリップの残りを新しいサブグループとして作成 + // Geometry の作成 + GeometryArray geometry = createGeometryArray(groupCoordinates, groupTexCoordinates, groupNormals, + subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + // Object3D の作成 + return new LeafModel(subGroupName, geometry, subGroupAp); + } + + /** + * ストリップのインデックス情報からジオメトリーを作成 + * @param groupCoordinates 頂点座標の値のリスト + * @param groupTexCoordinates テクスチャ座標の値のリスト + * @param groupNormals 法線ベクトルの値のリスト + * @param subGroupCoordinateIndicies 頂点座標へのインデックス + * @param subGroupTexCoordIndicies テクスチャ座標へのインデックス + * @param subGroupNormalIndicies 法線ベクトルへのインデックス + * @return 作成したジオメトリー + */ + private static GeometryArray createGeometryArray( + ArrayList groupCoordinates, + ArrayList groupTexCoordinates, + ArrayList groupNormals, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + int vertexCount = 0; + int indexCount = 0; + for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { + ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); + vertexCount += coordinateIndicies.size(); + indexCount += (coordinateIndicies.size() - 2) * 3; + } + int vertexFormat = IndexedGeometryArray.COORDINATES; + if (subGroupTexCoordIndicies.size() > 0) { + vertexFormat |= IndexedGeometryArray.TEXTURE_COORDINATE_2; + } + if (subGroupNormalIndicies.size() > 0) { + vertexFormat |= IndexedGeometryArray.NORMALS; + } + IndexedGeometryArray geometry = new IndexedTriangleArray(vertexCount, vertexFormat, indexCount); + int vertexNum = 0; + int index = 0; + for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { + ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); + ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); + ArrayList normalIndicies = subGroupNormalIndicies.get(n); + Vector3f c = groupCoordinates.get(coordinateIndicies.get(0)); + geometry.setCoordinate(vertexNum, new float[]{c.x, c.y, c.z}); + c = groupCoordinates.get(coordinateIndicies.get(1)); + geometry.setCoordinate(vertexNum + 1, new float[]{c.x, c.y, c.z}); + if (texCoordIndicies.size() > 0) { + Vector2f t = groupTexCoordinates.get(texCoordIndicies.get(0)); + geometry.setTextureCoordinate(vertexNum, new float[]{t.x, t.y}); + t = groupTexCoordinates.get(texCoordIndicies.get(1)); + geometry.setTextureCoordinate(vertexNum + 1, new float[]{t.x, t.y}); + } + if (normalIndicies.size() > 0) { + Vector3f nr = groupNormals.get(normalIndicies.get(0)); + geometry.setNormal(vertexNum, nr); + nr = groupNormals.get(normalIndicies.get(1)); + geometry.setNormal(vertexNum + 1, nr); + } + int firstCount = vertexNum; + vertexNum += 2; + for (int i = 2; i < coordinateIndicies.size(); i++) { + c = groupCoordinates.get(coordinateIndicies.get(i)); + geometry.setCoordinate(vertexNum, new float[]{c.x, c.y, c.z}); + geometry.setCoordinateIndex(index, firstCount); + geometry.setCoordinateIndex(index + 1, vertexNum - 1); + geometry.setCoordinateIndex(index + 2, vertexNum); + if (texCoordIndicies.size() > 0) { + Vector2f t = groupTexCoordinates.get(texCoordIndicies.get(i)); + geometry.setTextureCoordinate(vertexNum, new float[]{t.x, t.y}); + geometry.setTextureCoordinateIndex(index, firstCount); + geometry.setTextureCoordinateIndex(index + 1, vertexNum - 1); + geometry.setTextureCoordinateIndex(index + 2, vertexNum); + } + if (normalIndicies.size() > 0) { + Vector3f nr = groupNormals.get(normalIndicies.get(i)); + geometry.setNormal(vertexNum, nr); + geometry.setNormalIndex(index, firstCount); + geometry.setNormalIndex(index + 1, vertexNum - 1); + geometry.setNormalIndex(index + 2, vertexNum); + } + vertexNum += 1; + index += 3; + } + } + return geometry; +// ArrayList coordinateIndiciesMap = new ArrayList(); +// ArrayList texCoordIndiciesMap = new ArrayList(); +// ArrayList normalIndiciesMap = new ArrayList(); +// int indexCount = 0; +// int[] stripIndexCounts = new int[subGroupCoordinateIndicies.size()]; +// for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { +// ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); +// ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); +// ArrayList normalIndicies = subGroupNormalIndicies.get(n); +// indexCount += coordinateIndicies.size(); +// stripIndexCounts[n] = coordinateIndicies.size(); +// for (int i = 0; i < coordinateIndicies.size(); i++) { +// int c = coordinateIndicies.get(i); +// int coordinateIndex = coordinateIndiciesMap.indexOf(c); +// if (coordinateIndex == -1) { +// coordinateIndex = coordinateIndiciesMap.size(); +// coordinateIndiciesMap.add(c); +// } +// coordinateIndicies.set(i, coordinateIndex); +// if (texCoordIndicies.size() > 0) { +// int t = texCoordIndicies.get(i); +// int texCoordIndex = texCoordIndiciesMap.indexOf(t); +// if (texCoordIndex == -1) { +// texCoordIndex = texCoordIndiciesMap.size(); +// texCoordIndiciesMap.add(t); +// } +// texCoordIndicies.set(i, texCoordIndex); +// } +// if (normalIndicies.size() > 0) { +// int nr = normalIndicies.get(i); +// int normalIndex = normalIndiciesMap.indexOf(nr); +// if (normalIndex == -1) { +// normalIndex = normalIndiciesMap.size(); +// normalIndiciesMap.add(nr); +// } +// normalIndicies.set(i, normalIndex); +// } +// } +// } +// int vertexCount = coordinateIndiciesMap.size(); +// int vertexFormat = IndexedGeometryArray.COORDINATES; +// if (texCoordIndiciesMap.size() > 0) { +// if (vertexCount < texCoordIndiciesMap.size()) vertexCount = texCoordIndiciesMap.size(); +// vertexFormat |= IndexedGeometryArray.TEXTURE_COORDINATE_2; +// } +// if (normalIndiciesMap.size() > 0) { +// if (vertexCount < normalIndiciesMap.size()) vertexCount = normalIndiciesMap.size(); +// vertexFormat |= IndexedGeometryArray.NORMALS; +// } +// IndexedGeometryArray geometry = new IndexedTriangleFanArray(vertexCount, vertexFormat, indexCount, stripIndexCounts); +// for (int n = 0; n < coordinateIndiciesMap.size(); n++) { +// Vector3f c = groupCoordinates.get(coordinateIndiciesMap.get(n)); +// geometry.setCoordinate(n, new float[]{c.x, c.y, c.z}); +// } +// for (int n = 0; n < texCoordIndiciesMap.size(); n++) { +// Vector2f t = groupTexCoordinates.get(texCoordIndiciesMap.get(n)); +// geometry.setTextureCoordinate(n, new float[]{t.x, t.y}); +// } +// for (int n = 0; n < normalIndiciesMap.size(); n++) { +// Vector3f nr = groupNormals.get(normalIndiciesMap.get(n)); +// geometry.setNormal(n, nr); +// } +// int index = 0; +// for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { +// ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); +// ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); +// ArrayList normalIndicies = subGroupNormalIndicies.get(n); +// for (int i = 0; i < coordinateIndicies.size(); i++) { +// geometry.setCoordinateIndex(index, coordinateIndicies.get(i)); +// if (texCoordIndicies.size() > 0) { +// geometry.setTextureCoordinateIndex(index, texCoordIndicies.get(i)); +// } +// if (normalIndicies.size() > 0) { +// geometry.setNormalIndex(index, normalIndicies.get(i)); +// } +// index++; +// } +// } +// return geometry; + } + + /** + * MTLファイルの読み込み + * @param res リソース + * @param fileName ファイル名 + * @return 材質名から表面属性オブジェクトへのマッピング + * @throws IOException + * @throws ModelFileFormatException + */ + private static HashMap loadMtlFile(Resources res, String fileName) throws IOException, ModelFileFormatException { + InputStream in = res.getAssets().open(fileName); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + HashMap appearances = new HashMap(); + String materialName; + Appearance ap = null; + Material m = null; + + String line = null; + while ((line = br.readLine()) != null) { + line.trim(); + String data[] = line.split(" "); + if (data[0].equals("newmtl")) { + if (data.length < 1) { + System.out.println("Expected material's name"); + throw new ModelFileFormatException(); + } + materialName = data[1]; + m = new Material(); + ap = new Appearance(); + ap.setMaterial(m); + appearances.put(materialName, ap); + } else if (data[0].equals("Kd")) { + if (data.length < 4) { + System.out.println("Expected diffuse color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setDiffuseColor(r, g, b); + } else if (data[0].equals("Ka")) { + if (data.length < 4) { + System.out.println("Expected ambient color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setAmbientColor(r, g, b); + } else if (data[0].equals("Ks")) { + if (data.length < 4) { + System.out.println("Expected specular color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setSpecularColor(r, g, b); + } else if (data[0].equals("Tr")) { + if (data.length < 4) { + System.out.println("Expected emissive color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setEmissiveColor(r, g, b); + } else if (data[0].equals("Ns")) { + if (data.length < 2) { + System.out.println("Expected shiness value"); + throw new ModelFileFormatException(); + } + float s = Float.parseFloat(data[1]); + m.setShininess(s); + } else if (data[0].equals("map_Kd")) { + if (data.length < 2) { + System.out.println("Expected texture file's name"); + throw new ModelFileFormatException(); + } + // テクスチャファイル読み込み + try { + String dir = new File(fileName).getParent(); + String texFileName = line.substring(line.indexOf(" ") + 1); + String texFilePath = new File(dir, texFileName).getPath(); + InputStream inTex = res.getAssets().open(texFilePath); + TextureLoader texLoader = new TextureLoader(inTex, TextureLoader.BY_REFERENCE | TextureLoader.Y_UP); + ap.setTexture(texLoader.getTexture()); + inTex.close(); + } catch (IOException e) { + System.out.println("Not exist texture file: " + line.substring(line.indexOf(" ") + 1)); + } + } + } + return appearances; + } + + private static Integer readInt(BufferedInputStream bis) throws IOException { + byte[] intBin = new byte[4]; + if (bis.read(intBin, 3, 1) < 1) { + return null; + } + if (bis.read(intBin, 2, 1) < 1) { + return null; + } + if (bis.read(intBin, 1, 1) < 1) { + return null; + } + if (bis.read(intBin, 0, 1) < 1) { + return null; + } + return ByteBuffer.wrap(intBin).getInt(); + } + + private static Float readFloat(BufferedInputStream bis) throws IOException { + byte[] floatBin = new byte[4]; + if (bis.read(floatBin, 3, 1) < 1) { + return null; + } + if (bis.read(floatBin, 2, 1) < 1) { + return null; + } + if (bis.read(floatBin, 1, 1) < 1) { + return null; + } + if (bis.read(floatBin, 0, 1) < 1) { + return null; + } + return ByteBuffer.wrap(floatBin).getFloat(); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFileFormatException.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFileFormatException.java new file mode 100644 index 0000000..8351a69 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ModelFileFormatException.java @@ -0,0 +1,5 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +public class ModelFileFormatException extends Exception { + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Movable.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Movable.java new file mode 100644 index 0000000..6f09801 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Movable.java @@ -0,0 +1,7 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.framework.physics.Ground; + +public interface Movable extends Placeable { + public void motion(long interval, Ground ground); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/OBB.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/OBB.java new file mode 100644 index 0000000..ad86854 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/OBB.java @@ -0,0 +1,342 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.BoundingPolytope; +import com.google.ar.core.examples.java.common.java3d.BoundingSphere; +import com.google.ar.core.examples.java.common.java3d.Matrix4d; +import com.google.ar.core.examples.java.common.java3d.Point3d; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; +import com.google.ar.core.examples.java.common.java3d.Vector4d; + +import java.util.ArrayList; + + +public class OBB implements Cloneable { + public ArrayList vertexList = new ArrayList(); + public Vector4d[] plane = new Vector4d[6]; + public BoundingPolytope bp = null; + public static int edges[][] = { { 0, 1 }, { 0, 2 }, { 1, 3 }, { 2, 3 }, + { 0, 4 }, { 1, 5 }, { 3, 7 }, { 2, 6 }, { 4, 5 }, { 4, 6 }, + { 5, 7 }, { 6, 7 } }; + static boolean[][] inside = new boolean[8][6]; + static boolean[] inside2 = new boolean[8]; + + public OBB() { + } + + public OBB(Vector3d v1, Vector3d v2, Vector3d v3, Vector3d v4, + Vector3d v5, Vector3d v6, Vector3d v7, Vector3d v8) { + vertexList.add(v1); + vertexList.add(v2); + vertexList.add(v3); + vertexList.add(v4); + vertexList.add(v5); + vertexList.add(v6); + vertexList.add(v7); + vertexList.add(v8); + createPlanes(); + } + + public void addVertex(Vector3d v) { + vertexList.add(v); + } + + public Vector3d getVertex(int i) { + return vertexList.get(i); + } + + public BoundingSphere getBoundingSphere() { + double radius = 0.0; + Point3d p = new Point3d(); + Vector3d cv = new Vector3d(); + for (int i = 0; i < vertexList.size() - 1; i++) { + for (int j = i + 1; j < vertexList.size(); j++) { + Vector3d v = new Vector3d(); + v.sub(vertexList.get(i), vertexList.get(j)); + if (radius < v.length()) { + radius = v.length(); + cv.add(vertexList.get(i), vertexList.get(j)); + cv.scale(0.5); + p.x = cv.x; + p.y = cv.y; + p.z = cv.z; + } + } + } + BoundingSphere s = new BoundingSphere(p, radius / 2); + return s; + } + + /** + * 頂点情報を元に面および境界多面体を生成する + */ + public void createPlanes() { + // 面の作成 + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + Vector3d v3 = new Vector3d(); + Vector4d[] plane = new Vector4d[6]; + + // 0231 + v1 = vertexList.get(0); + v2.sub(vertexList.get(2), vertexList.get(0)); + v3.sub(vertexList.get(1), vertexList.get(0)); + Vector3d n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[0] = new Vector4d(); + plane[0].set(n.x, n.y, n.z, -n.dot(v1)); + + + // 0154 + v1 = vertexList.get(0); + v2.sub(vertexList.get(1), vertexList.get(0)); + v3.sub(vertexList.get(4), vertexList.get(0)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[1] = new Vector4d(); + plane[1].set(n.x, n.y, n.z, -n.dot(v1)); + + // 1375 + v1 = vertexList.get(1); + v2.sub(vertexList.get(3), vertexList.get(1)); + v3.sub(vertexList.get(5), vertexList.get(1)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[2] = new Vector4d(); + plane[2].set(n.x, n.y, n.z, -n.dot(v1)); + + // 4576 + v1 = vertexList.get(6); + v2.sub(vertexList.get(4), vertexList.get(6)); + v3.sub(vertexList.get(7), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[3] = new Vector4d(); + plane[3].set(n.x, n.y, n.z, -n.dot(v1)); + + // 0462 + v1 = vertexList.get(6); + v2.sub(vertexList.get(2), vertexList.get(6)); + v3.sub(vertexList.get(4), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[4] = new Vector4d(); + plane[4].set(n.x, n.y, n.z, -n.dot(v1)); + + // 2673 + v1 = vertexList.get(6); + v2.sub(vertexList.get(7), vertexList.get(6)); + v3.sub(vertexList.get(2), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[5] = new Vector4d(); + plane[5].set(n.x, n.y, n.z, -n.dot(v1)); + + this.plane = plane; + + bp = new BoundingPolytope(); + bp.setPlanes(plane); + } + + /** + * 平面との衝突判定 + * @param plane 衝突判定の対象となる平面 + * @return 衝突判定の結果(衝突していない場合はnull) + */ + public CollisionResult intersect(Vector4d plane) { + int i = 0; + boolean inside = false; + boolean outside = false; + int count = 0; + Vector3d center = new Vector3d(0, 0, 0); + double l = Math.sqrt(plane.x * plane.x + plane.y * plane.y + plane.z * plane.z); + double length; + double deepest = 0.0; + Vector3d v; + for (i = 0; i < vertexList.size(); i++) { + v = vertexList.get(i); + if (GeometryUtility.inside(v, plane)) { + inside = true; + length = -(v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w) / l; + if (length > deepest + GeometryUtility.TOLERANCE) { + center.set(v); + count = 1; + deepest = length; + } else if (length >= deepest - GeometryUtility.TOLERANCE) { + center.add(v); + count++; + } + } else { + outside = true; + } + } + + if (!inside || !outside) { + // 全頂点が外側か全頂点が内側の場合 + return null; + } + + center.scale(1.0 / count); + + CollisionResult cr = new CollisionResult(); + cr.length = deepest; + cr.collisionPoint.setX(center.x); + cr.collisionPoint.setY(center.y); + cr.collisionPoint.setZ(center.z); + cr.normal.setX(plane.x / l); + cr.normal.setY(plane.y / l); + cr.normal.setZ(plane.z / l); + + return cr; + } + + /** + * OBBとの衝突判定 + * @param o 衝突判定の対象となるOBB + * @return 衝突判定の結果(衝突していない場合はnull) + */ + public CollisionResult intersect(OBB o) { + // oの各頂点がthisの各面の内側にあるかを調べる + // i:頂点 j:面 + for (int i = 0; i < o.vertexList.size(); i++) { + for (int j = 0; j < plane.length; j++) { + inside[i][j] = GeometryUtility.inside(o.vertexList.get(i), plane[j]); + } + } + + boolean collision = false; + int count = 0; + Vector3d center = new Vector3d(0, 0, 0); + + // oの各頂点がthisに包含されているか? + for (int v = 0; v < 8; v++) { + boolean f1 = false; + for (int p = 0; p < 6; p++) { + f1 = inside[v][p]; + if (!f1) { + break; + } + } + inside2[v] = f1; + if (f1) { + collision = true; + center.add(o.vertexList.get(v)); + count++; + } + } + + if (!collision) { + // 辺が相手の面と交わっているか? + for (int p = 0; p < 6; p++) { + Vector3d intersection = null; + for (int e = 0; e < 12; e++) { + if (inside[edges[e][0]][p] == !inside[edges[e][1]][p]) { + intersection = GeometryUtility.intersect( + plane[p], + o.vertexList.get(edges[e][0]), + o.vertexList.get(edges[e][1])); + boolean bInside = true; + for (int p2 = 0; p2 < 6; p2++) { + if (p != p2 + && !GeometryUtility.inside(intersection, plane[p2])) { + bInside = false; + break; + } + } + if (bInside) { + collision = true; + center.add(intersection); + count++; + } + } + } + } + } + + if (!collision) + return null; + + center.scale(1.0 / count); + + CollisionResult cr = new CollisionResult(); + + double lMin = 0; + int cm = -1; + Vector3d normal = null; + for (int m = 0; m < plane.length; m++) { + Vector3d n = new Vector3d(plane[m].x, plane[m].y, plane[m].z); + double l = -(center.x * plane[m].x + center.y * plane[m].y + center.z + * plane[m].z + plane[m].w) / n.length(); + if (lMin > l || cm == -1) { + lMin = l; + cm = m; + normal = n; + } + } + cr.length = lMin; + cr.collisionPoint.setVector3d(center); + cr.normal.setX(plane[cm].x / normal.length()); + cr.normal.setY(plane[cm].y / normal.length()); + cr.normal.setZ(plane[cm].z / normal.length()); + + return cr; + + } + + public Object clone() { + OBB obb = new OBB(); + obb.plane = new Vector4d[6]; + for (int i = 0; i < plane.length; i++) { + obb.plane[i] = (Vector4d) plane[i].clone(); + } + for (int i = 0; i < vertexList.size(); i++) { + obb.vertexList.add((Vector3d) vertexList.get(i).clone()); + } + obb.bp = new BoundingPolytope(obb.plane); + + // System.out.println("veretexListSIZE:"+vertexList.size()); + return obb; + } + + public void transform(Transform3D t) { + // TODO Auto-generated method stub + bp.transform(t); + bp.getPlanes(plane); + // for(int i = 0; i" + plane[i].x + "," + plane[i].y + "," + + // plane[i].z + "," + plane[i].w); + // } + for (int i = 0; i < vertexList.size(); i++) { + // System.out.println("vertex"); + // System.out.print(vertexList.get(i).x + "," + vertexList.get(i).y + // + "," + vertexList.get(i).z); + Matrix4d mat4d = new Matrix4d(); + t.get(mat4d); + double x = mat4d.m00 * vertexList.get(i).x + mat4d.m01 + * vertexList.get(i).y + mat4d.m02 * vertexList.get(i).z + + mat4d.m03; + double y = mat4d.m10 * vertexList.get(i).x + mat4d.m11 + * vertexList.get(i).y + mat4d.m12 * vertexList.get(i).z + + mat4d.m13; + double z = mat4d.m20 * vertexList.get(i).x + mat4d.m21 + * vertexList.get(i).y + mat4d.m22 * vertexList.get(i).z + + mat4d.m23; + vertexList.get(i).x = x; + vertexList.get(i).y = y; + vertexList.get(i).z = z; + // t.transform(vertexList.get(i)); + // System.out.println("-->" + vertexList.get(i).x + "," + + // vertexList.get(i).y + "," + vertexList.get(i).z); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Object3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Object3D.java new file mode 100644 index 0000000..813a70e --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Object3D.java @@ -0,0 +1,535 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Appearance; +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.BoundingSphere; +import com.google.ar.core.examples.java.common.java3d.Box; +import com.google.ar.core.examples.java.common.java3d.Cone; +import com.google.ar.core.examples.java.common.java3d.Cylinder; +import com.google.ar.core.examples.java.common.java3d.Geometry; +import com.google.ar.core.examples.java.common.java3d.GeometryStripArray; +import com.google.ar.core.examples.java.common.java3d.IndexedGeometryArray; +import com.google.ar.core.examples.java.common.java3d.Leaf; +import com.google.ar.core.examples.java.common.java3d.Node; +import com.google.ar.core.examples.java.common.java3d.Primitive; +import com.google.ar.core.examples.java.common.java3d.Quat4d; +import com.google.ar.core.examples.java.common.java3d.Shape3D; +import com.google.ar.core.examples.java.common.java3d.Sphere; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; +import com.google.ar.core.examples.java.common.java3d.TriangleArray; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; + + +public class Object3D extends BaseObject3D { + public String name; + public TransformGroup pos; + public TransformGroup rot; + public TransformGroup scale; + public Object3D[] children = new Object3D[0]; + private Position3D position; + protected Quaternion3D quaternion; + + private OBB obb = new OBB(); +// private boolean bLOD = false; +// private LOD lodNode = null; + + public BoundingSphere bs = null; + private UndoBuffer undoBuffer = new UndoBuffer(); + + public Object3D(String name, Primitive prim) { + init(name); + center.addChild(prim); + } + + public Object3D(String name, Leaf node) { + init(name); + center.addChild(node); + } + + public Object3D(String name, Geometry g, Appearance a) { + this(name, new Shape3D(g, a)); + } + + public Object3D(String name, Object3D[] children) { + init(name); + this.children = children; + for (int i = 0; i < children.length; i++) { + center.addChild(children[i].getTransformGroupToPlace()); + } + } + + public Object3D(String name, Object3D[] children, Transform3D defaultTransform) { + this(name, children); + if (defaultTransform == null) return; + pos.setTransform(defaultTransform); + } + + // コピーコンストラクタ + public Object3D(Object3D obj) { + this.name = new String(obj.name); + if (obj.position != null) { + this.position = new Position3D(obj.position); + } else { + this.position = new Position3D(); + } + if (obj.getQuaternion() != null) { + this.quaternion = new Quaternion3D(obj.getQuaternion()); + } else { + this.quaternion = new Quaternion3D(); + } + Transform3D transPos = new Transform3D(); + obj.pos.getTransform(transPos); + this.pos = new TransformGroup(transPos); + Transform3D transRot = new Transform3D(); + obj.rot.getTransform(transRot); + this.rot = new TransformGroup(transRot); + Transform3D transScale = new Transform3D(); + obj.scale.getTransform(transScale); + this.scale = new TransformGroup(transScale); + Transform3D transCenter = new Transform3D(); + obj.center.getTransform(transCenter); + this.center = new TransformGroup(transCenter); + this.pos.addChild(this.rot); + this.rot.addChild(this.scale); + this.scale.addChild(this.center); +// this.pos.addChild(this.scale); +//// this.rot.addChild(this.scale); +// this.scale.addChild(this.center); + if (obj.hasChildren()) { + this.children = new Object3D[obj.children.length]; + for (int i = 0; i < obj.children.length; i++) { + this.children[i] = new Object3D(obj.children[i]); + this.center.addChild(this.children[i].pos); + } + } else { + this.children = new Object3D[0]; + Enumeration nodes = obj.getPrimitiveNodes(); + while (nodes.hasMoreElements()) { + Node node = nodes.nextElement(); + if (node != null && node instanceof Shape3D) { + Shape3D shape = (Shape3D)node; + Appearance a = (Appearance)shape.getAppearance().cloneNodeComponent(); + this.center.addChild(new Shape3D((Geometry) shape.getGeometry(), a)); + } else if (node != null && node instanceof Primitive) { + Primitive primitive = (Primitive)node; + primitive = (Primitive)primitive.cloneTree(); + this.center.addChild(primitive); + } + } + } + if (obj.obb != null) { + this.obb = obj.obb; + } + if (obj.bs != null) { + this.bs = obj.bs; + } + this.undoBuffer = new UndoBuffer(obj.undoBuffer); + } + + // 自分を複製する(クローンを作る) + public Object3D duplicate() { + Object3D obj = new Object3D(this); + return obj; + } + + protected void init(String name) { + this.name = new String(name); + pos = new TransformGroup(); + rot = new TransformGroup(); + scale = new TransformGroup(); + center = new TransformGroup(); + pos.addChild(rot); + rot.addChild(scale); + scale.addChild(center); + } + + public Node getPrimitiveNode() { + if (hasChildren()) return null; +// if (!bLOD) { + return (Node)center.getChild(0); + // TODO 未実装 +// } else { +// // LOD の場合 +// return lodNode.getSwitch(0).getChild(0); +// } + } + + public Enumeration getPrimitiveNodes() { + if (hasChildren()) return null; +// if (!bLOD) { + return (Enumeration)center.getAllChildren(); + // TODO 未実装 +// } else { +// // LOD の場合 +// return lodNode.getSwitch(0).getAllChildren(); +// } + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return pos; + } + + public void setPosition(Position3D p) { + position = (Position3D) p.clone(); + Vector3d v = new Vector3d(p.getX(), p.getY(), p.getZ()); + Transform3D trans = new Transform3D(); + trans.set(v); + pos.setTransform(trans); + } + + public Position3D getPosition3D() { + return (Position3D) position.clone(); + } + + public void scale(double s) { + Transform3D trans = new Transform3D(); + trans.setScale(s); + scale.setTransform(trans); + } + + public void scale(double x, double y, double z) { + Transform3D trans = new Transform3D(); + trans.setScale(new Vector3d(x, y, z)); + scale.setTransform(trans); + } + + // キャラクターを回転させる + public void rotate(double vx, double vy, double vz, double a) { + AxisAngle4d t = new AxisAngle4d(vx, vy, vz, a); + Transform3D trans = new Transform3D(); + trans.setRotation(t); + rot.setTransform(trans); + } + + public void apply(Property3D p, boolean enableUndo) { + if (enableUndo) { + undoBuffer.push(p); + } + p.applyTo(this); + } + +// undoするポイントを設定する。 + public void setUndoMark() { + undoBuffer.setUndoMark(); + } + +// undoする。 + public void undo() { + Iterator iterator = undoBuffer.undo().iterator(); + while (iterator.hasNext()) { + Property3D p = iterator.next(); + p.applyTo(this); + } + } + + // objectに子供がいるかどうかを調べる + public boolean hasChildren() { + if (this.children != null && this.children.length > 0) + return true; + else + return false; + } + + /** + * 部品オブジェクトを探す + * @param partName 部品名 + * @return partName を持つ部品オブジェクト + */ + public Object3D getPart(String partName) { + if (partName.equals(this.name)) return this; + + for (int i = 0; i < children.length; i++) { + if (children[i] != null) { + Object3D obj = children[i].getPart(partName); + if (obj != null) return obj; + } + } + return null; + } + + protected void rotate(Quat4d quat) { + Transform3D trans = new Transform3D(); + trans.setRotation(quat); + rot.setTransform(trans); + } + + public void accept(ObjectVisitor objectVisitor) { + int i; + objectVisitor.preVisit(this); + if (children != null) { + for (i = 0; i < children.length; i++) + children[i].accept(objectVisitor); + } + objectVisitor.postVisit(this); + } + + // Quaternion3D の applyTo() 以外からは呼ばないこと + void setQuaternion(Quaternion3D quaternion) { + this.quaternion = quaternion; + rotate(quaternion.getQuat()); + } + + public Quaternion3D getQuaternion() { + return quaternion; + } + + public OBB getOBB(int pattern) { + if (obb == null || obb.bp == null) { + Node node = getPrimitiveNode(); + if (node == null) return null; + if (node instanceof Box) { + // Boxの場合 + Box box = ((Box)node); + double xDim = box.getXdimension(); + double yDim = box.getYdimension(); + double zDim = box.getZdimension(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Cylinder) { + // Cylinderの場合 + Cylinder cylinder = ((Cylinder)node); + double xDim = cylinder.getRadius(); + double yDim = cylinder.getHeight() / 2; + double zDim = cylinder.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Cone) { + // Coneの場合 + Cone cone = ((Cone)node); + double xDim = cone.getRadius(); + double yDim = cone.getHeight() / 2; + double zDim = cone.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Sphere) { + // Sphereの場合 + Sphere sphere = ((Sphere)node); + double xDim = sphere.getRadius(); + double yDim = sphere.getRadius(); + double zDim = sphere.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else { + if (!(node instanceof Shape3D)) return null; + // Shape3Dの場合 + Shape3D shape = (Shape3D)node; + double coordinate[] = new double[3]; + + // OBBの作成に用いる頂点列の取得 + ArrayList vertex3DList = new ArrayList(); + if (shape.getGeometry() instanceof IndexedGeometryArray) { + // IndexedGeometryArrayの場合 + IndexedGeometryArray iga = (IndexedGeometryArray) shape.getGeometry(); + for (int i = 0; i < iga.getIndexCount(); i++) { + iga.getCoordinates(iga.getCoordinateIndex(i), coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } else if (shape.getGeometry() instanceof GeometryStripArray) { + // GeometryStripArrayの場合 + GeometryStripArray gsa = (GeometryStripArray) shape.getGeometry(); + for (int i = 0; i < gsa.getVertexCount(); i++) { + gsa.getCoordinates(i, coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } else if (shape.getGeometry() instanceof TriangleArray) { + // TriangleArrayの場合 + TriangleArray tra = (TriangleArray) shape.getGeometry(); + for (int i = 0; i < tra.getVertexCount(); i++) { + tra.getCoordinates(i, coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } + + if (pattern == 0) { + // 最大面積法 + Vector3d cv1 = new Vector3d(); + Vector3d cv2 = new Vector3d(); + + double cvMax = 0.0; + Vector3d axis1 = new Vector3d(); // 3D頂点から求めた法線ベクトル + Vector3d axis2 = new Vector3d(); // 2D頂点から求めた法線ベクトル + Vector3d axis3 = new Vector3d(); // axis1,axis2の外積から求めた法線ベクトル + + // 面積を3D頂点リストから求め、法線を求める処理 + for (int i = 0; i < vertex3DList.size(); i += 3) { + cv1.sub(vertex3DList.get(i + 2), vertex3DList.get(i)); + cv2.sub(vertex3DList.get(i + 1), vertex3DList.get(i)); + Vector3d cv = new Vector3d(); + cv.cross(cv1, cv2); + if (i == 0 || cv.length() >= cvMax) { + cvMax = cv.length(); + axis1 = cv; + } + } + + ProjectionResult pr1 = GeometryUtility.projection3D(vertex3DList, axis1); + // 辺のを2D頂点リストから求め、法線を求める処理 + for (int i = 0; i < pr1.vertexList.size() - 1; i++) { + Vector3d cv = new Vector3d(); + cv.sub(vertex3DList.get(i + 1), vertex3DList.get(i)); + if (i == 0 || cv.length() >= cvMax) { + cvMax = cv.length(); + axis2 = cv; + } + } + + + ProjectionResult pr2 = GeometryUtility.projection2D(pr1.vertexList, axis2); + // axis1,axis2で外積でaxis3を求める。 + axis3.cross(axis1, axis2); + + ProjectionResult pr3 = GeometryUtility.projection2D(pr1.vertexList, axis3); + AxisResult ar = new AxisResult(axis1, axis2, axis3); + + // ここから最大面積法で求めた点を取得していく処理 + + // OBBの生成 + obb = new OBB(); + + // No.1 + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.2 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.3 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.4 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.5 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.6 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.7 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.8 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // 面および境界多面体の生成 + obb.createPlanes(); + } else { + // AABB + double minX, maxX, minY, maxY, minZ, maxZ; + minX = maxX = vertex3DList.get(0).x; + minY = maxY = vertex3DList.get(0).y; + minZ = maxZ = vertex3DList.get(0).z; + for (int n = 1; n < vertex3DList.size(); n++) { + Vector3d v = vertex3DList.get(n); + if (minX > v.x) minX = v.x; + if (maxX < v.x) maxX = v.x; + if (minY > v.y) minY = v.y; + if (maxY < v.y) maxY = v.y; + if (minZ > v.z) minZ = v.z; + if (maxZ < v.z) maxZ = v.z; + } + obb = new OBB(new Vector3d(minX, minY, minZ), + new Vector3d(minX, maxY, minZ), + new Vector3d(minX, minY, maxZ), + new Vector3d(minX, maxY, maxZ), + new Vector3d(maxX, minY, minZ), + new Vector3d(maxX, maxY, minZ), + new Vector3d(maxX, minY, maxZ), + new Vector3d(maxX, maxY, maxZ)); + } + } + } + return obb; + } + + private class AxisResult { + Vector3d axis1; + Vector3d axis2; + Vector3d axis3; + + AxisResult(Vector3d a1, Vector3d a2, Vector3d a3) { + axis1 = (Vector3d) a1.clone(); + axis2 = (Vector3d) a2.clone(); + axis3 = (Vector3d) a3.clone(); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ObjectVisitor.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ObjectVisitor.java new file mode 100644 index 0000000..9f2b88c --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ObjectVisitor.java @@ -0,0 +1,57 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Transform3D; + +import java.util.ArrayList; + +/** + * オブジェクトの階層構造をトラバースするビジター + * @author 新田直也 + * + */ +public abstract class ObjectVisitor { + /** + * 根から訪問節点までのパス上に存在する変換行列 + */ + protected ArrayList stackList = new ArrayList(); + + /* + * 節点(オブジェクト)を訪問する直前に呼ばれるメソッド + * @param obj 訪問節点 + */ + public abstract void preVisit(Object3D obj); + /** + * 節点(オブジェクトを訪問した直後に呼ばれるメソッド) + * @param obj 訪問節点 + */ + public abstract void postVisit(Object3D obj); + + /** + * 1階層潜ったときに変換行列を増やす + * @param obj 新しく訪問した節点 + */ + protected void pushTransform(Object3D obj) { + Transform3D transPos = new Transform3D(); + obj.pos.getTransform(transPos); + stackList.add(transPos); + Transform3D transRot = new Transform3D(); + obj.rot.getTransform(transRot); + stackList.add(transRot); + Transform3D transScale = new Transform3D(); + obj.scale.getTransform(transScale); + stackList.add(transScale); + Transform3D transCenter = new Transform3D(); + obj.center.getTransform(transCenter); + stackList.add(transCenter); + } + + /** + * 1階層復帰したときに変換行列を 減らす + */ + protected void popTransform() { + for (int i = 0; i < 4; i++) { + stackList.remove(stackList.size() - 1); + } + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Placeable.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Placeable.java new file mode 100644 index 0000000..2f9cdf1 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Placeable.java @@ -0,0 +1,13 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.TransformGroup; + +/** + * 配置できるもの全て + * @author 新田直也 + * + */ +public interface Placeable { + abstract TransformGroup getTransformGroupToPlace(); + abstract BaseObject3D getBody(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Position3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Position3D.java new file mode 100644 index 0000000..82e4736 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Position3D.java @@ -0,0 +1,151 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +/** + * 座標値を表す + * @author 新田直也 + * + */ +public class Position3D extends Property3D { + private double x; + private double y; + private double z; + + // コンストラクタ + public Position3D() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public Position3D(double px, double py, double pz) { + x = px; + y = py; + z = pz; + } + + public Position3D(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + } + + // property3Dの抽象クラスを埋める + public void applyTo(Object3D o) { + o.setPosition(this); + } + + // コピーコンストラクタ + public Position3D(Position3D p) { + this.x = p.x; + this.y = p.y; + this.z = p.z; + } + + public Position3D set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + // 加算 + public Position3D add(double x, double y, double z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + // 加算 + public Position3D add(Position3D p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + return this; + } + + // 加算 + public Position3D add(Vector3d v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + // 減算 + public Position3D sub(double x, double y, double z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + // 減算 + public Position3D sub(Position3D p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + return this; + } + + // 減算 + public Position3D sub(Vector3d v) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + return this; + } + + // スカラー倍 + public Position3D mul(double d) { + this.x *= d; + this.y *= d; + this.z *= d; + return this; + } + + public Position3D setX(double x) { + this.x = x; + return this; + } + + public double getX() { + return x; + } + + public Position3D setY(double y) { + this.y = y; + return this; + } + + public double getY() { + return y; + } + + public Position3D setZ(double z) { + this.z = z; + return this; + } + + public double getZ() { + return z; + } + + public Vector3d getVector3d() { + return new Vector3d(x, y, z); + } + + public Position3D setVector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + return this; + } + + public Position3D clone() { + return new Position3D(this); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ProjectionResult.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ProjectionResult.java new file mode 100644 index 0000000..6828697 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/ProjectionResult.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.framework.model3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +public class ProjectionResult { + double max = 0.0; + double min = 0.0; + ArrayList vertexList = new ArrayList(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Property3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Property3D.java new file mode 100644 index 0000000..7ff0388 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Property3D.java @@ -0,0 +1,7 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +public abstract class Property3D{ + public abstract void applyTo(Object3D o); + + public abstract Property3D clone(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Quaternion3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Quaternion3D.java new file mode 100644 index 0000000..5122f54 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Quaternion3D.java @@ -0,0 +1,107 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.Quat4d; + + +public class Quaternion3D extends Property3D { + private Quat4d quaternion; + + // コンストラクタ============================= + public Quaternion3D() { + AxisAngle4d aa = new AxisAngle4d(0.0, 0.0, 1.0, 0.0); + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(AxisAngle4d aa) { + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(double x, double y, double z, double w) { + AxisAngle4d aa = new AxisAngle4d(x, y, z, w); + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(Quaternion3D q) { + this.quaternion = (Quat4d) q.getQuat().clone(); + } + + public Quat4d getQuat() { + return quaternion; + } + + // ゲッタ================================= + public double getX() { + return quaternion.x; + } + + public double getY() { + return quaternion.y; + } + + public double getZ() { + return quaternion.z; + } + + public double getW() { + return quaternion.w; + } + + public Quaternion3D getInterpolate(Quaternion3D q, double alpha) { + quaternion.interpolate(q.getQuat(), alpha); + return this; + } + + public AxisAngle4d getAxisAngle() { + AxisAngle4d axisAngle = new AxisAngle4d(); + axisAngle.set(quaternion); + return axisAngle; + } + + // セッタ================================= + public Quaternion3D setQuaternion(double x, double y, double z, double w) { + quaternion = new Quat4d(x, y, z, w); + return this; + } + + public Quaternion3D setAxisAngle(double x, double y, double z, double w) { + AxisAngle4d aa = new AxisAngle4d(x, y, z, w); + quaternion = new Quat4d(); + quaternion.set(aa); + return this; + } + + public Quaternion3D add(AxisAngle4d aa) { + Quat4d q = new Quat4d(); + q.set(aa); + q.mul(quaternion); + quaternion = q; + return this; + } + + public Quaternion3D mul(Quaternion3D q) { + quaternion.mul(q.getQuat()); + return this; + } + + public void applyTo(Object3D o) { + o.setQuaternion(this); + } + + @Override + public Quaternion3D clone() { + return new Quaternion3D(this); + } + + public double norm() { + return quaternion.w * quaternion.w + quaternion.x * quaternion.x + quaternion.y * quaternion.y + quaternion.z * quaternion.z; + } + + public Quaternion3D normalize() { + quaternion.normalize(); + return this; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Terrain3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Terrain3D.java new file mode 100644 index 0000000..01eacd4 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Terrain3D.java @@ -0,0 +1,449 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.java3d.Appearance; +import com.google.ar.core.examples.java.common.java3d.BoundingBox; +import com.google.ar.core.examples.java.common.java3d.Point3d; +import com.google.ar.core.examples.java.common.java3d.Shape3D; +import com.google.ar.core.examples.java.common.java3d.TriangleStripArray; +import com.google.ar.core.examples.java.common.java3d.Vector3d; +import com.google.ar.core.examples.java.common.java3d.Vector3f; + +public class Terrain3D extends BaseObject3D { + static final int MESH_SIZE = 4; + private Position3D origin; + private double sizeX = 0; + private double sizeZ = 0; + private double heightMap[][] = null; + private int meshX = 0; + private int meshZ = 0; + private TriangleStripArray triStripAttay = null; + + public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { + super(); + this.origin = origin; + this.sizeX = sizeX; + this.sizeZ = sizeZ; + this.heightMap = heightMap; + meshX = heightMap[0].length; + meshZ = heightMap.length; + + int stripVertexCounts[] = new int[meshZ - 1]; + for (int n = 0; n < meshZ - 1; n++) { + stripVertexCounts[n] = meshX * 2; + } + triStripAttay = new TriangleStripArray(meshX * 2 * (meshZ - 1), + TriangleStripArray.COORDINATES | TriangleStripArray.NORMALS, + stripVertexCounts); + int index = 0; + for (int z = 0; z < meshZ; z++) { + for (int x = 0; x < meshX; x++) { + // メッシュごとに三角形を2つづつ生成 + if (z < meshZ - 1) { + // 頂点座標の設定 + // 頂点の法線ベクトルの設定 + triStripAttay.setCoordinate(index, + new Point3d(origin.getX() + sizeX * (double)x, + origin.getY() + heightMap[z][x], + origin.getZ() + sizeZ * (double)z)); + Vector3f normal = calcNormal(z, x); + normal.normalize(); + triStripAttay.setNormal(index, normal); + + triStripAttay.setCoordinate(index + 1, + new Point3d(origin.getX() + sizeX * (double)x, + origin.getY() + heightMap[z + 1][x], + origin.getZ() + sizeZ * (double)(z + 1))); + normal = calcNormal(z + 1, x); + normal.normalize(); + triStripAttay.setNormal(index + 1, normal); + + index += 2; + } + } + } + Appearance a2 = (Appearance)a.cloneNodeComponent(); + Shape3D shape = new Shape3D(triStripAttay, a2); + center.addChild(shape); + } + + private Vector3f calcNormal(int z, int x) { + Vector3f normal = new Vector3f(); + if (x < meshX - 1) { + Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); + if (z < meshZ - 1) { + Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(zp, xp); + v1.normalize(); + normal.add(v1); + } + if (z > 0) { + Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(xp, zm); + v1.normalize(); + normal.add(v1); + } + } + if (x > 0) { + Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); + if (z < meshZ - 1) { + Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(xm, zp); + v1.normalize(); + normal.add(v1); + } + if (z > 0) { + Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(zm, xm); + v1.normalize(); + normal.add(v1); + } + } + return normal; + } + + public BoundingSurface[] getBoundingSurfaces() { + if (boundingSurfaces == null) { + if (triStripAttay == null) return null; + double coordinate1[] = new double[3]; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + + // GeometryArrayの習得 + if (triStripAttay instanceof TriangleStripArray) { + // IndexedTriangleStripArray の場合 + // 8 x 8 メッシュ単位でまとめる + BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; + int n = 0; + for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { + for (int i = 0; i < meshX - 1; i += MESH_SIZE) { + BoundingSurface parent = new BoundingSurface(); + double lowY = 1.0; + double highY = -1.0; + for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { + for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { + int index = (j + j2) * meshX * 2 + (i + i2) * 2; + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index + 1, coordinate2); + triStripAttay.getCoordinates(index + 2, coordinate3); + triStripAttay.getCoordinates(index + 3, coordinate4); + if (lowY > highY) { + lowY = highY = coordinate1[1]; + } else { + if (lowY > coordinate1[1]) { + lowY = coordinate1[1]; + } else if (highY < coordinate1[1]) { + highY = coordinate1[1]; + } + if (lowY > coordinate2[1]) { + lowY = coordinate2[1]; + } else if (highY < coordinate2[1]) { + highY = coordinate2[1]; + } + if (lowY > coordinate3[1]) { + lowY = coordinate3[1]; + } else if (highY < coordinate3[1]) { + highY = coordinate3[1]; + } + if (lowY > coordinate4[1]) { + lowY = coordinate4[1]; + } else if (highY < coordinate4[1]) { + highY = coordinate4[1]; + } + } + + // 1 x 1 メッシュ内のBoundingSurface + Vector3d v1 = new Vector3d(coordinate1); + Vector3d v2 = new Vector3d(coordinate2); + Vector3d v3 = new Vector3d(coordinate3); + Vector3d v4 = new Vector3d(coordinate4); + BoundingSurface bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 + bSurface.addVertex((Vector3d)v2.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); + parent.addChild(bSurface, false); + + bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 + bSurface.addVertex((Vector3d)v4.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); + parent.addChild(bSurface, true); + } + } + // 8 x 8 メッシュ単位のBoundingSurface + triStripAttay.getCoordinates(j * meshX * 2 + i * 2, coordinate1); + parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), + new Point3d(coordinate4[0], highY, coordinate4[2]))); + surfaces[n] = parent; + n++; + } + } + boundingSurfaces = surfaces; + } else { + return null; + } + } + return boundingSurfaces; + } + + public double getHeight(double x, double z) { + int i = (int)((x - origin.getX()) / sizeX); + int j = (int)((z - origin.getZ()) / sizeZ); + if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; + + int index = j * meshX * 2 + i * 2; + double coordinate1[] = new double[3]; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index + 1, coordinate2); + triStripAttay.getCoordinates(index + 2, coordinate3); + triStripAttay.getCoordinates(index + 3, coordinate4); + Vector3d v1 = new Vector3d(coordinate1); + Vector3d v2 = new Vector3d(coordinate2); + Vector3d v3 = new Vector3d(coordinate3); + Vector3d v4 = new Vector3d(coordinate4); + Vector3d p1 = new Vector3d(x, 0.0, z); + Vector3d p2 = new Vector3d(x, 1.0, z); + + double x2 = x - (double)i * sizeX - origin.getX(); + double z2 = z - (double)j * sizeZ - origin.getZ(); + if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { + // 1つめの三角形を通る場合 + Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); + return crossPoint.getY(); + } else { + // 2つめの三角形を通る場合 + Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); + return crossPoint.getY(); + } + } +} + +// +// ********** IndexedTriangleStripArray を使うバージョン ********** +// (IndexedTriangleStripArrayを用いるとメモリを節約できるが、シェーディングができないので注意) +// +//public class Terrain3D extends BaseObject3D { +// static final int MESH_SIZE = 8; +// private Position3D origin; +// private double sizeX = 0; +// private double sizeZ = 0; +// private double heightMap[][] = null; +// private int meshX = 0; +// private int meshZ = 0; +// private IndexedTriangleStripArray triStripAttay = null; +// +// public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { +// super(); +// this.origin = origin; +// this.sizeX = sizeX; +// this.sizeZ = sizeZ; +// this.heightMap = heightMap; +// meshX = heightMap[0].length; +// meshZ = heightMap.length; +// +// int stripVertexCounts[] = new int[meshZ - 1]; +// for (int n = 0; n < meshZ - 1; n++) { +// stripVertexCounts[n] = meshX * 2; +// } +// triStripAttay = new IndexedTriangleStripArray(meshX * meshZ, +// IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.NORMALS, +// meshX * 2 * (meshZ - 1), +// stripVertexCounts); +// int index = 0, index2 = 0, i1, i2; +// for (int z = 0; z < meshZ; z++) { +// for (int x = 0; x < meshX; x++) { +// // 頂点座標の設定 +// triStripAttay.setCoordinate(index, +// new Point3d(origin.getX() + sizeX * (double)x, +// origin.getY() + heightMap[z][x], +// origin.getZ() + sizeZ * (double)z)); +// // 頂点の法線ベクトルの設定 +// Vector3f normal = new Vector3f(); +// if (x < meshX - 1) { +// Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); +// if (z < meshZ - 1) { +// Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(zp, xp); +// v1.normalize(); +// normal.add(v1); +// } +// if (z > 0) { +// Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(xp, zm); +// v1.normalize(); +// normal.add(v1); +// } +// } +// if (x > 0) { +// Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); +// if (z < meshZ - 1) { +// Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(xm, zp); +// v1.normalize(); +// normal.add(v1); +// } +// if (z > 0) { +// Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(zm, xm); +// v1.normalize(); +// normal.add(v1); +// } +// } +// normal.normalize(); +// triStripAttay.setNormal(index, normal); +// index++; +// // メッシュごとに三角形を2つづつ生成 +// if (z < meshZ - 1) { +// i1 = z * meshX + x; +// i2 = (z + 1) * meshX + x; +// triStripAttay.setCoordinateIndices(index2, new int[]{i1, i2}); +// index2 += 2; +// } +// } +// } +// Appearance a2 = (Appearance)a.cloneNodeComponent(true); +// a2.setCapability(Appearance.ALLOW_MATERIAL_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_WRITE); +// a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE); +// Shape3D shape = new Shape3D(triStripAttay, a2); +// shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ); +// shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); +// center.addChild(shape); +// } +// +// public BoundingSurface[] getBoundingSurfaces() { +// if (boundingSurfaces == null) { +// if (triStripAttay == null) return null; +// double coordinate1[] = new double[3]; +// double coordinate2[] = new double[3]; +// double coordinate3[] = new double[3]; +// double coordinate4[] = new double[3]; +// +// // GeometryArrayの習得 +// if (triStripAttay instanceof IndexedTriangleStripArray) { +// // IndexedTriangleStripArray の場合 +// // 8 x 8 メッシュ単位でまとめる +// BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; +// int n = 0; +// for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { +// for (int i = 0; i < meshX - 1; i += MESH_SIZE) { +// BoundingSurface parent = new BoundingSurface(); +// double lowY = 1.0; +// double highY = -1.0; +// for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { +// for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { +// int index = (j + j2) * meshX * 2 + (i + i2) * 2; +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); +// if (lowY > highY) { +// lowY = highY = coordinate1[1]; +// } else { +// if (lowY > coordinate1[1]) { +// lowY = coordinate1[1]; +// } else if (highY < coordinate1[1]) { +// highY = coordinate1[1]; +// } +// if (lowY > coordinate2[1]) { +// lowY = coordinate2[1]; +// } else if (highY < coordinate2[1]) { +// highY = coordinate2[1]; +// } +// if (lowY > coordinate3[1]) { +// lowY = coordinate3[1]; +// } else if (highY < coordinate3[1]) { +// highY = coordinate3[1]; +// } +// if (lowY > coordinate4[1]) { +// lowY = coordinate4[1]; +// } else if (highY < coordinate4[1]) { +// highY = coordinate4[1]; +// } +// } +// +// // 1 x 1 メッシュ内のBoundingSurface +// Vector3d v1 = new Vector3d(coordinate1); +// Vector3d v2 = new Vector3d(coordinate2); +// Vector3d v3 = new Vector3d(coordinate3); +// Vector3d v4 = new Vector3d(coordinate4); +// BoundingSurface bSurface = new BoundingSurface(); +// bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 +// bSurface.addVertex((Vector3d)v2.clone()); +// bSurface.addVertex((Vector3d)v3.clone()); +// bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); +// parent.addChild(bSurface, false); +// +// bSurface = new BoundingSurface(); +// bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 +// bSurface.addVertex((Vector3d)v4.clone()); +// bSurface.addVertex((Vector3d)v3.clone()); +// bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); +// parent.addChild(bSurface, true); +// } +// } +// // 8 x 8 メッシュ単位のBoundingSurface +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(j * meshX * 2 + i * 2), coordinate1); +// parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), +// new Point3d(coordinate4[0], highY, coordinate4[2]))); +// surfaces[n] = parent; +// n++; +// } +// } +// boundingSurfaces = surfaces; +// } else { +// return null; +// } +// } +// return boundingSurfaces; +// } +// +// public double getHeight(double x, double z) { +// int i = (int)((x - origin.getX()) / sizeX); +// int j = (int)((z - origin.getZ()) / sizeZ); +// if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; +// +// int index = j * meshX * 2 + i * 2; +// double coordinate1[] = new double[3]; +// double coordinate2[] = new double[3]; +// double coordinate3[] = new double[3]; +// double coordinate4[] = new double[3]; +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); +// Vector3d v1 = new Vector3d(coordinate1); +// Vector3d v2 = new Vector3d(coordinate2); +// Vector3d v3 = new Vector3d(coordinate3); +// Vector3d v4 = new Vector3d(coordinate4); +// Vector3d p1 = new Vector3d(x, 0.0, z); +// Vector3d p2 = new Vector3d(x, 1.0, z); +// +// double x2 = x - (double)i * sizeX - origin.getX(); +// double z2 = z - (double)j * sizeZ - origin.getZ(); +// if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { +// // 1つめの三角形を通る場合 +// Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); +// return crossPoint.getY(); +// } else { +// // 2つめの三角形を通る場合 +// Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); +// return crossPoint.getY(); +// } +// } +//} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/UndoBuffer.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/UndoBuffer.java new file mode 100644 index 0000000..04d7a10 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/UndoBuffer.java @@ -0,0 +1,48 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; + +public class UndoBuffer { + @SuppressWarnings("unchecked") + private ArrayList> list = new ArrayList>(); + @SuppressWarnings("unchecked") + private Hashtable nowCondition = new Hashtable(); + private static final int max = 3; + + UndoBuffer() { + } + + UndoBuffer(UndoBuffer another) { + this.list = (ArrayList>)another.list.clone(); + this.nowCondition = (Hashtable)another.nowCondition.clone(); + } + + public void push(Property3D p) { + nowCondition.put(p.getClass(), p.clone()); + } + + @SuppressWarnings("unchecked") + public void setUndoMark() { + if(list.size() == max) { + list.remove(max - 1); + } + list.add(0, (Hashtable) nowCondition.clone()); + } + + @SuppressWarnings("unchecked") + public Collection undo() { + if(list.size() != 0) { + nowCondition = list.remove(0); + return nowCondition.values(); + } + else { + return new Hashtable().values(); + } + } + + public void clear() { + + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Universe.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Universe.java new file mode 100644 index 0000000..ffe835a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/model3D/Universe.java @@ -0,0 +1,142 @@ +package com.google.ar.core.examples.java.common.framework.model3D; + +import com.google.ar.core.examples.java.common.framework.physics.Ground; +import com.google.ar.core.examples.java.common.java3d.BranchGroup; +import com.google.ar.core.examples.java.common.java3d.Group; +import com.google.ar.core.examples.java.common.java3d.Leaf; +import com.google.ar.core.examples.java.common.java3d.Light; +import com.google.ar.core.examples.java.common.java3d.Node; +import com.google.ar.core.examples.java.common.java3d.Primitive; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; + +import java.util.ArrayList; + +public class Universe { + private BranchGroup root = null; + private ArrayList lights = new ArrayList(); + private BackgroundBox skyBox = null; + private Ground ground = null; + private ArrayList movableList = new ArrayList(); + + public Universe() { + root = new BranchGroup(); + } + + public void render(IViewer3D viewer) { + viewer.update(lights, skyBox); + render(viewer, root); + } + + private void render(IViewer3D viewer, Node node) { + if (node instanceof Group) { + if (node instanceof Primitive) { + viewer.draw((Primitive) node); + } else { + if (node instanceof TransformGroup) { + Transform3D transform = new Transform3D(); + ((TransformGroup) node).getTransform(transform); + // +// Vector3d vec = new Vector3d(1.0, 1.1, 1.0); +// transform.setScale(vec); +// AxisAngle4d aa = new AxisAngle4d(0.0, 0.0, 1.0, 1.0); +// transform.setRotation(aa); + // + viewer.pushTransform(transform); + for (int i = 0; i < ((Group) node).numChildren(); i++) { + Node node2 = ((Group) node).getChild(i); + render(viewer, node2); + } + viewer.popTransform(); + } else { + for (int i = 0; i < ((Group) node).numChildren(); i++) { + Node node2 = ((Group) node).getChild(i); + render(viewer, node2); + } + } + } + } else if (node instanceof Leaf) { + viewer.draw(node); + } + } + + public void update(long interval){ + for(int i = 0; i < movableList.size(); i++){ + Movable movable = movableList.get(i); + movable.motion(interval,ground); + } + } + + /** + * オブジェクトを配置する + * + * @param obj + * 配置するオブジェクト + */ + public void place(Placeable obj) { + if(obj instanceof Ground){ + ground = (Ground)obj; + } + if(obj instanceof Movable){ + movableList.add((Movable)obj); + } + place(obj.getTransformGroupToPlace()); + } + + public void place(Node node) { + root.addChild(node); + } + + /** + * 後で取り除けるようにオブジェクトを配置する + * + * @param obj + * 配置するオブジェクト + */ + public void placeDisplacable(Placeable obj) { + placeDisplacable(obj.getTransformGroupToPlace()); + } + + private void placeDisplacable(Node node) { + root.addChild(node); + } + + /** + * 光源の追加 + * + * @param light + * 追加する光源 + */ + public void placeLight(Light light) { + root.addChild(light); + getLights().add(light); + } + + /** + * スカイボックスの追加 + * @param skyBox 追加するスカイボックス + */ + public void placeSkyBox(BackgroundBox skyBox) { + root.addChild(skyBox); + this.skyBox = skyBox; + } + + /** + * オブジェクトを可能ならば取り除く + * + * @param obj + * 取り除くオブジェクト + */ + public void displace(Placeable obj) { + displace(obj.getTransformGroupToPlace()); + } + + private void displace(Node node) { + root.removeChild(node); + } + + public ArrayList getLights() { + return lights; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/network/HttpAsyncConnection.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/network/HttpAsyncConnection.java new file mode 100644 index 0000000..7565fa5 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/network/HttpAsyncConnection.java @@ -0,0 +1,151 @@ +package com.google.ar.core.examples.java.common.framework.network; + +import android.app.Activity; +import android.os.AsyncTask; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +abstract public class HttpAsyncConnection extends AsyncTask { + + private HttpURLConnection conn; + private StringBuffer message = new StringBuffer(); + protected Activity activity = null; + private static String clientSessionID = null; + + + public HttpAsyncConnection(String url) { + try { + conn = (HttpURLConnection) new URL(url).openConnection(); + } catch (MalformedURLException e) { + + e.printStackTrace(); + } catch (IOException e) { + + e.printStackTrace(); + } + } + public HttpAsyncConnection(String url, Activity activity) { + this(url); + this.activity = activity; + try { + conn = (HttpURLConnection) new URL(url).openConnection(); + } catch (MalformedURLException e) { + + e.printStackTrace(); + } catch (IOException e) { + + e.printStackTrace(); + } + } + + @Override + protected String doInBackground(Void... urls) { + doPost(); + return doGet(); + } + + // Parse XML + public void onPostExecute(String result) { + // CLLog.debug(""+result); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(result)); + Document dom = builder.parse(is); + Element root = dom.getDocumentElement(); + receive(root); + }catch(Exception e) { + e.printStackTrace(); + } + } + + abstract protected void receive(Element root); + + // request + public void doPost() { + try { + conn.setReadTimeout(10000 /* milliseconds */); + conn.setConnectTimeout(15000 /* milliseconds */); + // POST or GET + conn.setRequestMethod("POST"); + //conn.setRequestMethod("GET"); + + conn.setDoOutput(true); + if(clientSessionID != null) { + conn.setRequestProperty("Cookie", clientSessionID); + } + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8")); + writer.write(message.toString()); + message.delete(0,message.length()); + writer.flush(); + writer.close(); + + + if(clientSessionID == null ) { + clientSessionID = conn.getHeaderField("Set-Cookie"); + + } + } catch (IOException e) { + notConnection(); + e.printStackTrace(); + } + } + + // response + public String doGet() { + BufferedReader reader; + try { + reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + StringBuilder builder = new StringBuilder(); + while((line = reader.readLine()) != null) + builder.append(line); + reader.close(); + return builder.toString(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + } + + public void notConnection(){}; + + /** + * param set + * @param key + * @param value + */ + public void addParam(String key, String value) { + if(message.length() > 0) { + message.append("&"); + } + message.append(key+"="+value); + } + + public Activity getActivity() { + return activity; + } + + public void setActivity(Activity activity) { + this.activity = activity; + + + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/AngularVelocity3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/AngularVelocity3D.java new file mode 100644 index 0000000..15bcb43 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/AngularVelocity3D.java @@ -0,0 +1,85 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Property3D; +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class AngularVelocity3D extends Property3D{ + private double x; + private double y; + private double z; + + public AngularVelocity3D(AngularVelocity3D w) { + x = w.x; + y = w.y; + z = w.z; + } + + public AngularVelocity3D(double x,double y,double z){ + this.x = x; + this.y = y; + this.z = z; + } + + public AngularVelocity3D(){ + x = 0.0; + y = 0.0; + z = 0.0; + } + + public void applyTo(Object3D o){ + ((Solid3D)o).setAngularVelocity(this); + } + + public double getX() { + // TODO Auto-generated method stub + return this.x; + } + + public double getY() { + // TODO Auto-generated method stub + return this.y; + } + + public double getZ() { + // TODO Auto-generated method stub + return this.z; + } + + public AngularVelocity3D add(double x, double y, double z){ + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public AngularVelocity3D add(Vector3d v) { + // TODO Auto-generated method stub + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + public Vector3d getVector3d(){ + return new Vector3d(x,y,z); + } + + public AxisAngle4d getAxisAngle4d() { + double l = getVector3d().length(); + if (l <= 0.00001) { + return new AxisAngle4d(0, 0, 1.0, 0.0); + } + return new AxisAngle4d(x / l, y / l, z / l, l); + } + + @Override + public Property3D clone() { + // TODO Auto-generated method stub + return new AngularVelocity3D(this); + } +} + + diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingBoxVisitor.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingBoxVisitor.java new file mode 100644 index 0000000..371ab9f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingBoxVisitor.java @@ -0,0 +1,93 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.OBB; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.ObjectVisitor; +import com.google.ar.core.examples.java.common.java3d.BoundingSphere; + +import java.util.ArrayList; + + +public class BoundingBoxVisitor extends ObjectVisitor { + private ArrayList obbList = new ArrayList(); // 全構成要素のOBBのリスト + private ArrayList bsStack = new ArrayList(); // オブジェクトの階層毎のBoundingSphereのスタック + private String partName = null; // 部品を指定する場合に使う + private boolean inPart = false; + + public BoundingBoxVisitor() { + partName = null; + } + + public BoundingBoxVisitor(String partName) { + this.partName = partName; + } + + public void preVisit(Object3D obj) { + pushTransform(obj); + if (partName != null && obj.name.equals(partName)) { + inPart = true; + } + if (obj.hasChildren() && obj.bs == null) { + // 子供がいる場合、下の階層用にnullをpushする + bsStack.add(null); + } + } + + public void postVisit(Object3D obj) { + int pattern = 2; + if (!obj.hasChildren()) { + // 葉の場合 + OBB obb = obj.getOBB(pattern); + if (obb != null) { + if (obj.bs == null) { + obj.bs = obb.getBoundingSphere(); + } + + obb = (OBB)obb.clone(); + BoundingSphere bs = (BoundingSphere)obj.bs.clone(); + for (int i = stackList.size() - 1; i >= 0; i--) { + obb.transform(stackList.get(i)); + bs.transform(stackList.get(i)); + } + if (partName == null || partName.length() == 0 || inPart) { + obbList.add(obb); // Transform3Dを適応させたBoundsをboundsListに追加 + int stackTop = bsStack.size() - 1; + if (bs != null && stackTop >= 0) { + if (bsStack.get(stackTop) == null) { + // その階層の最初のオブジェクトの場合、nullを置き換え + bsStack.set(stackTop, bs); + } else { + // その階層の2番目以降のオブジェクトの場合、結合 + bsStack.get(stackTop).combine(bs); + } + } + } + } + } else { + // 子供がいる場合 + int stackTop = bsStack.size() - 1; + if (obj.bs == null) { + // 下の階層の結合結果をpopして利用する + obj.bs = bsStack.remove(stackTop); + stackTop--; + } + if (obj.bs != null && stackTop >= 0) { + if (bsStack.get(stackTop) == null) { + // その階層の最初のオブジェクトの場合、nullを置き換え + bsStack.set(stackTop, obj.bs); + } else { + // その階層の2番目以降のオブジェクトの場合、結合 + bsStack.get(stackTop).combine(obj.bs); + } + } + } + popTransform(); + if (partName != null && obj.name.equals(partName)) { + inPart = false; + } + } + + public ArrayList getObbList() { + return obbList; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingSurfaceVisitor.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingSurfaceVisitor.java new file mode 100644 index 0000000..ffbf0fd --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/BoundingSurfaceVisitor.java @@ -0,0 +1,59 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.BaseObject3D; +import com.google.ar.core.examples.java.common.framework.model3D.BoundingSurface; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.ObjectVisitor; + +import java.util.ArrayList; + +/** + * 地面の衝突判定用のボリュームを生成するためのビジター + * @author 新田直也 + * + */ +public class BoundingSurfaceVisitor extends ObjectVisitor { + private ArrayList boundingSurfaceList = new ArrayList(); + public BoundingSurfaceVisitor() { + boundingSurfaceList.add(new BoundingSurface()); + } + + public void preVisit(Object3D obj) { + pushTransform(obj); + if (obj.hasChildren()) { + boundingSurfaceList.add(new BoundingSurface()); + } + } + + public void postVisit(Object3D obj) { + if (!obj.hasChildren()) { + // 葉の場合 + BoundingSurface[] s = (BoundingSurface[]) obj.getBoundingSurfaces().clone(); + for (int i = 0; i < s.length; i++) { + s[i] = (BoundingSurface) s[i].clone(); + for (int j = stackList.size() - 1; j >= 0; j--) { + s[i].transform(stackList.get(j)); + } + boundingSurfaceList.get(boundingSurfaceList.size() - 1).addChild(s[i], true); // Transform3Dを適応させたBoundsをsurfaceListに追加 + } + } else { + BoundingSurface child = boundingSurfaceList.remove(boundingSurfaceList.size() - 1); + BoundingSurface parent = boundingSurfaceList.get(boundingSurfaceList.size() - 1); + parent.addChild(child, true); + } + popTransform(); + } + + public void baseVisit(BaseObject3D obj) { + BoundingSurface parent = boundingSurfaceList.get(boundingSurfaceList.size() - 1); + BoundingSurface[] s = (BoundingSurface[]) obj.getBoundingSurfaces().clone(); + for (int i = 0; i < s.length; i++) { + s[i] = (BoundingSurface) s[i].clone(); + parent.addChild(s[i], true); + } + } + + public BoundingSurface getBoundingSurface() { + return boundingSurfaceList.get(0); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Force3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Force3D.java new file mode 100644 index 0000000..6c52fa2 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Force3D.java @@ -0,0 +1,35 @@ +package com.google.ar.core.examples.java.common.framework.physics; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class Force3D { + double x; + double y; + double z; + public static final Force3D ZERO = new Force3D( 0.0, 0.0, 0.0); + + public Force3D(double x,double y,double z){ + this.x = x; + this.y = y; + this.z = z; + } + + public Force3D(Vector3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + public Vector3d getVector3d(){ + return new Vector3d(x,y,z); + } + + public void add(Force3D f) { + x += f.x; + y += f.y; + z += f.z; + } + + public double getSeverity() { + return getVector3d().length(); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Ground.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Ground.java new file mode 100644 index 0000000..f0e2de4 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Ground.java @@ -0,0 +1,56 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.BaseObject3D; +import com.google.ar.core.examples.java.common.framework.model3D.BoundingSurface; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Placeable; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; + + +/** + * 地面などの(基本的に動かない)構造物を表すオブジェクト + * @author 新田直也 + * + */ +public class Ground implements Placeable { + private BaseObject3D groundObj = null; + private BoundingSurface boundingSurface = null; // 衝突判定用ボリュームのキャッシュ + + public Ground(BaseObject3D obj) { + groundObj = obj; + } + + public BaseObject3D getBody() { + return groundObj; + } + + public void updateBody(BaseObject3D obj) { + groundObj = obj; + boundingSurface = null; + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return groundObj.getTransformGroupToPlace(); + } + + /** + * 衝突判定用のボリュームを取得する + * @return 衝突判定用ボリューム(階層化されている場合がある) + */ + BoundingSurface getBoundingSurface() { + if (boundingSurface == null) { + // キャッシュに何も積まれていない場合のみ計算する + BoundingSurfaceVisitor surfaceVisitor = new BoundingSurfaceVisitor(); + if (groundObj instanceof Object3D) { + // Object3Dの場合階層構造をたどる + ((Object3D)groundObj).accept(surfaceVisitor); + } else { + // BaseObject3dの場合階層構造がない + surfaceVisitor.baseVisit(groundObj); + } + boundingSurface = surfaceVisitor.getBoundingSurface(); + } + return boundingSurface; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Inertia3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Inertia3D.java new file mode 100644 index 0000000..d8c6c88 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Inertia3D.java @@ -0,0 +1,54 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.OBB; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class Inertia3D { + double ixx; + double iyy; + double izz; + static final Inertia3D ZERO = new Inertia3D( 0.0, 0.0, 0.0); + + public Inertia3D(double x,double y,double z){ + this.ixx = x; + this.iyy = y; + this.izz = z; + } + + public Inertia3D(Vector3d v) { + this.ixx = v.x; + this.iyy = v.y; + this.izz = v.z; + } + + public Inertia3D(Solid3D obj) { + BoundingBoxVisitor visitor = new BoundingBoxVisitor(); + obj.accept(visitor); + if (visitor.getObbList().size() == 1) { + OBB obb = visitor.getObbList().get(0); + Vector3d vx = new Vector3d(); + vx.sub(obb.vertexList.get(4), obb.vertexList.get(0)); + + Vector3d vy = new Vector3d(); + vy.sub(obb.vertexList.get(1), obb.vertexList.get(0)); + + Vector3d vz = new Vector3d(); + vz.sub(obb.vertexList.get(2), obb.vertexList.get(0)); + +// System.out.println("vx:" + vx + ",vy:" + vy + ",vz:" + vz); + this.ixx = obj.mass * (1.0/12.0 * (Math.pow(vy.length(), 2.0) + Math.pow(vz.length(), 2.0))); + this.iyy = obj.mass * (1.0/12.0 * (Math.pow(vx.length(), 2.0) + Math.pow(vz.length(), 2.0))); + this.izz = obj.mass * (1.0/12.0 * (Math.pow(vx.length(), 2.0) + Math.pow(vy.length(), 2.0))); + } else { + this.ixx = obj.mass; + this.iyy = obj.mass; + this.izz = obj.mass; + } + } + + public Vector3d getVector3d() { + return new Vector3d(ixx, iyy, izz); + } +} + diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicalSystem.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicalSystem.java new file mode 100644 index 0000000..7e294fd --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicalSystem.java @@ -0,0 +1,152 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.CollisionResult; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + + +public class PhysicalSystem { + + public ArrayList objects = new ArrayList(); + + // 物体の挿入 + public int add(Solid3D s) { + objects.add(s); + return objects.size() - 1; + } + + // 物体の運動 + public void motion(int id, long interval, Force3D f, Position3D appPoint, Ground ground) { + ArrayList forces[] = new ArrayList[objects.size()]; + ArrayList appPoints[] = new ArrayList[objects.size()]; + for (int i = 0; i < objects.size(); i++) { + forces[i] = new ArrayList(); + appPoints[i] = new ArrayList(); + } + + // id番目の外力の計算 + forces[id].add(f); + appPoints[id].add(appPoint); +// objects.get(id).move(interval, f, appPoint); + + double l; // ばねの伸び + + Force3D penalty = new Force3D(0.0, 0.0, 0.0); // ペナルティ法によるペナルティの作用の力 + Force3D inversepenalty; // + // //ペナルティ法によるペナルティの反作用の力 + CollisionResult cr; + Solid3D s; + for (int n = 0; n < objects.size(); n++) { + // 重力の計算 + s = objects.get(n); + forces[n].add(PhysicsUtility.getGravity(s)); + appPoints[n].add(s.getGravityCenter()); +// objects.get(n).move(interval, +// PhysicsFacade.getGravity(objects.get(n)), +// objects.get(n).getGravityCenter()); // 重力の計算 + // 地面との当たり判定 + cr = PhysicsUtility.doesIntersect(s, ground); + // 地面に物体がめり込んでいる場合 + if (cr != null) { + double gk = 5000.0; // 地面でのばね係数 + double e = 1.0; // 地面での跳ね返り時の抵抗係数 + double b = 300.0; + l = cr.length; + // <作用の力の計算> + // ペナルティの変数 +// Vector3d v = cr.normal; +// v.scale(gk * l); + // 作用点ベクトルの作成 + Vector3d r = cr.collisionPoint.getVector3d(); + // (作用点-重心)ベクトル + r.sub(s.getGravityCenter().getVector3d()); + // 角速度ベクトルの作成 + Vector3d angVel = s.getAngularVelocity().getVector3d(); + // 角速度ベクトルと(作用点-重心)ベクトルの外積計算 + angVel.cross(angVel, r); + // 速度ベクトル+角速度ベクトルと(作用点-重心)ベクトルの外積計算 + Vector3d relV = s.getVelocity().getVector3d(); + // 相対速度ベクトルの作成 + relV.add(angVel); + Vector3d v = cr.normal; +//System.out.println(r + "," + (gk * l) + "," + (- relV.dot(v) * b)); + // ペナルティの大きさ決定 + v.scale(gk * l - relV.dot(v) * b); + penalty = new Force3D(v); + + // 作用の力による運動 + forces[n].add(penalty); + appPoints[n].add(cr.collisionPoint); +// objects.get(n).move(interval, penalty, cr.collisionPoint); + } + // 地面に物体がめり込んでいない場合 + else { + } + for (int m = 0; m < n; m++) { + Solid3D s1 = objects.get(n); + Solid3D s2 = objects.get(m); + cr = PhysicsUtility.checkCollision(s1, null, s2, null); + // 物体がめり込んでいる場合 + if (cr != null) { + double sk = 5000; // 物体でのばね係数 + double e = 0.2; // 物体での跳ね返り時の抵抗係数 + double b = 300.0; + l = cr.length; + // <作用の力の計算> + // 作用点ベクトルの作成 + // s1に関する計算 + // s1の角速度ベクトルの作成 + Vector3d r = cr.collisionPoint.getVector3d(); + r.sub(s1.getGravityCenter().getVector3d()); + Vector3d s1AngVel = s1.getAngularVelocity().getVector3d(); + s1AngVel.cross(s1AngVel, r); + // s1の速度ベクトルの作成 + Vector3d s1RelV = s1.getVelocity().getVector3d(); + // s1の速度ベクトル+s1の角速度ベクトルと(作用点-s1の重心)ベクトルの外積計算 + s1RelV.add(s1AngVel); + // s2に関する計算 + // s2の角速度ベクトルの作成 + r = cr.collisionPoint.getVector3d(); + r.sub(s2.getGravityCenter().getVector3d()); + Vector3d s2AngVel = s2.getAngularVelocity().getVector3d(); + s2AngVel.cross(s2AngVel, r); + // s2の速度ベクトルの作成 + Vector3d s2RelV = s2.getVelocity().getVector3d(); + // s2の速度ベクトル+s2の角速度ベクトルと(作用点-s2の重心)ベクトルの外積計算 + s2RelV.add(s2AngVel); + // 相対速度ベクトルの作成 + s1RelV.sub(s2RelV); + // ペナルティの大きさ決定 + Vector3d v = (Vector3d)cr.normal.clone(); +//System.out.println(r + "," + (sk * l) + "," + (- relV.dot(v) * b)); + v.scale(sk * l - s1RelV.dot(v) * b); + penalty = new Force3D(v); + + // 反作用の力の計算 + v.scale(-1); + inversepenalty = new Force3D(v); + + // 作用の力による物体の移動 + forces[n].add(penalty); + appPoints[n].add(cr.collisionPoint); +// s1.move(interval, penalty, cr.collisionPoint); + + // 反作用の力による物体の移動 + forces[m].add(inversepenalty); + appPoints[m].add(cr.collisionPoint); +// s2.move(interval, inversepenalty, cr.collisionPoint); + } +// // 物体がめり込んでいない場合 +// else { +// s2.move(interval, f, s2.getGravityCenter()); +// s1.move(interval, f, s1.getGravityCenter()); +// } + } + } + for (int n2 = 0; n2 < objects.size(); n2++) { + objects.get(n2).move(interval, forces[n2], appPoints[n2]); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicsUtility.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicsUtility.java new file mode 100644 index 0000000..18cc8de --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/PhysicsUtility.java @@ -0,0 +1,222 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.BoundingSurface; +import com.google.ar.core.examples.java.common.framework.model3D.CollisionResult; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.BoundingSphere; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + + +/** + * 物理演算のコア + * @author 新田直也 + * + */ +public class PhysicsUtility { + public static final double GRAVITY = 9.8; // 重力の加速度 + public static final Vector3d horizon = new Vector3d(1.0, 0.0, 0.0); + public static final Vector3d vertical = new Vector3d(0.0, 1.0, 0.0); + + public static Vector3d gravityDirection = new Vector3d(0.0, 1.0, 0.0); + + /** + * 物体に加わる重力を求める + * @param body 対象物体 + * @return bodyに加わる重力 + */ + public static Force3D getGravity(Solid3D body) { + return new Force3D(gravityDirection.x * -body.mass * GRAVITY, gravityDirection.y * -body.mass * GRAVITY, gravityDirection.z * -body.mass * GRAVITY); + } + + /** + * @param v + * :単位ベクトル、あるいはゼロベクトルを引数で渡すこと + */ + public static void setGravityDirection(Vector3d v) { + gravityDirection = new Vector3d(v.x, v.y, v.z); + } + + // モーメントの計算 + static Vector3d calcMoment(Force3D f, Position3D gravityCenter, + Position3D applicationPoint) { + Vector3d v1 = applicationPoint.getVector3d(); + Vector3d v2 = gravityCenter.getVector3d(); + v1.sub(v2); + + Vector3d cv = new Vector3d(); + Vector3d fv = f.getVector3d(); + cv.cross(v1, fv); + return cv; + + } + + /** + * 物体の反発係数から衝突時に加わる力を求める + * @param interval 衝突している時間 + * @param nor 物体がぶつかった面の宝仙ベクトル + * @param solid 物体 + * @return 衝突時に加わる力 + */ + public static Force3D calcForce(long interval, Vector3d nor, Solid3D solid) { + double f1 = 0.0; + Vector3d vf = new Vector3d(solid.getVelocity().getX(), solid + .getVelocity().getY(), solid.getVelocity().getZ()); + f1 = solid.mass * (vf.length() + solid.e * vf.length()) + / ((double) interval / 1000.0); + nor.scale(f1); + Force3D f = new Force3D(nor.x, nor.y, nor.z); + return f; + } + + /** + * 物体と地面との衝突判定 + * @param obj 物体 + * @param ground 地面 + * @return 衝突情報(nullのとき衝突なし) + */ + public static CollisionResult doesIntersect(Solid3D obj, Ground ground) { + if (ground == null) return null; + CollisionResult cr = null; + BoundingSurface boundingSurface = ground.getBoundingSurface(); + + // BoundingSphereを使って大雑把に衝突判定を行う + ArrayList boundingSurfaceList = null; + if (obj.bs != null) { + BoundingSphere bs = (BoundingSphere) (obj.bs.clone()); + Transform3D t3d = new Transform3D(); + obj.center.getTransform(t3d); + bs.transform(t3d); + obj.scale.getTransform(t3d); + bs.transform(t3d); + obj.rot.getTransform(t3d); + bs.transform(t3d); + obj.pos.getTransform(t3d); + bs.transform(t3d); + // 粗い衝突判定を行う(最上位のBoundingSurfaceとBoundingSphereの間で) + boundingSurfaceList = boundingSurface.intersect(bs); + bs = null; + t3d = null; + } + + if (obj.bs == null) { + // BoundingSphere がまだ作られていななかった場合、 + // 詳細な衝突判定のために、最上位の全 BoundingSurface を取得する + if (boundingSurfaceList == null) boundingSurfaceList = new ArrayList(); + boundingSurfaceList.add(boundingSurface); + } + + if (boundingSurfaceList.size() > 0) { + // 粗い衝突判定で衝突していた場合、OBBの集合を用いてより詳しい衝突判定を行う + // (BoundingSphere がまだ作られていない場合、OBB の作成と同時に BoundingSphere を作成される) + BoundingBoxVisitor obbVisitor = new BoundingBoxVisitor(); + obj.accept(obbVisitor); + for (int i = 0; i < obbVisitor.getObbList().size(); i++) { + // OBBと衝突判定をする場合は、地面を多角形のまま扱う + for (int j = 0; j < boundingSurfaceList.size(); j++) { + cr = boundingSurfaceList.get(j).intersect(obbVisitor.getObbList().get(i)); + if (cr != null) { + return cr; + } + } + } + obbVisitor = null; + } + return null; + } + + /** + * 物体同士の衝突判定 + * @param obj1 物体1 + * @param part1 判定する物体1の部分の名称 + * @param obj2 物体2 + * @param part2 判定する物体2の部分の名称 + * @return 衝突情報(nullのとき衝突なし) + */ + public static CollisionResult checkCollision(Object3D obj1, String part1, + Object3D obj2, String part2) { + CollisionResult cr = null; + + // BoundingSphereを使って大雑把に衝突判定を行う + boolean f = false; + if (obj1.bs != null && obj2.bs != null) { + // sol1 の BoundingSphere を計算 + BoundingSphere bs1 = (BoundingSphere) (obj1.bs.clone()); + Transform3D t3d = new Transform3D(); + obj1.center.getTransform(t3d); + bs1.transform(t3d); + obj1.scale.getTransform(t3d); + bs1.transform(t3d); + obj1.rot.getTransform(t3d); + bs1.transform(t3d); + obj1.pos.getTransform(t3d); + bs1.transform(t3d); + + // sol2 の BoundingSphere を計算 + BoundingSphere bs2 = (BoundingSphere) (obj2.bs.clone()); + obj2.center.getTransform(t3d); + bs2.transform(t3d); + obj2.scale.getTransform(t3d); + bs2.transform(t3d); + obj2.rot.getTransform(t3d); + bs2.transform(t3d); + obj2.pos.getTransform(t3d); + bs2.transform(t3d); + + // BoundingSphere 同士の衝突判定 + if (bs1.intersect(bs2)) { + f = true; + } + t3d = null; + bs1 = null; + bs2 = null; + } + if (f || obj1.bs == null || obj2.bs == null) { + BoundingBoxVisitor visitor1 = new BoundingBoxVisitor(part1); + BoundingBoxVisitor visitor2 = new BoundingBoxVisitor(part2); + obj1.accept(visitor1); + obj2.accept(visitor2); + // OBB o1 = obj1.getOBB(0); + // OBB o2 = obj2.getOBB(0); + // cr = o1.intersect(o2); + int i, j; + for (i = 0; i < visitor1.getObbList().size(); i++) { + for (j = 0; j < visitor2.getObbList().size(); j++) { + cr = visitor2.getObbList().get(j).intersect(visitor1.getObbList().get(i)); + // System.out.println("flg:"+flg); + if (cr != null) { + // Vector3d v = new Vector3d(0,0,0); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getX()); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getY()); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getZ()); + // return v; + return cr; + } else { + cr = visitor1.getObbList().get(i).intersect( + visitor2.getObbList().get(j)); + if (cr != null) { + cr.normal.scale(-1.0); + // Vector3d v = new Vector3d(0,0,0); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getX()); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getY()); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getZ()); + // return v; + return cr; + } + } + } + } + } + // System.out.println("Yes!!"); + return null; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/ProjectionResult.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/ProjectionResult.java new file mode 100644 index 0000000..4b4cac2 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/ProjectionResult.java @@ -0,0 +1,11 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +public class ProjectionResult { + double max = 0.0; + double min = 0.0; + ArrayList vertexList = new ArrayList(); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Solid3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Solid3D.java new file mode 100644 index 0000000..7ec6690 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Solid3D.java @@ -0,0 +1,176 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Quaternion3D; +import com.google.ar.core.examples.java.common.java3d.AxisAngle4d; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +/** + * 物理的な振る舞いをする物体(剛体)を表す + * @author 新田直也 + * + */ +public class Solid3D extends Object3D { + private Velocity3D velocity; + private AngularVelocity3D angularvelocity; + private Position3D gravityCenter = getPosition3D(); + public double e = 1.0; + public double mass = 10; + private Inertia3D inertia = null; + + // コピーコンストラクタ + public Solid3D(Object3D obj) { + super(obj); + velocity = new Velocity3D(); + angularvelocity = new AngularVelocity3D(); + inertia = new Inertia3D(this); + } + + public Solid3D(Object3D obj, double mass) { + super(obj); + velocity = new Velocity3D(); + angularvelocity = new AngularVelocity3D(); + this.mass = mass; + inertia = new Inertia3D(this); + } + + public Solid3D(Solid3D solid) { + super(solid); + if (solid.velocity != null) { + velocity = new Velocity3D(solid.velocity); + } else { + velocity = new Velocity3D(); + } + if (solid.angularvelocity != null) { + angularvelocity = new AngularVelocity3D(solid.angularvelocity); + } else { + angularvelocity = new AngularVelocity3D(); + } + mass = solid.mass; + inertia = new Inertia3D(this); + } + + /** + * 力学運動の計算(加わる力が1つの場合) + * @param interval 単位時間 + * @param f 力 + * @param applicationPoint 力の作用点 + */ + public void move(long interval, Force3D f, Position3D applicationPoint) { + // モーメントの計算 + Vector3d moment = PhysicsUtility.calcMoment(f, getGravityCenter(), + applicationPoint); + moveSub(interval, f, moment); + } + + /** + * 力学運動の計算(同時に複数の力が加わる場合) + * @param interval 単位時間 + * @param forces 力(複数) + * @param appPoints それぞれの力の作用点 + */ + public void move(long interval, ArrayList forces, + ArrayList appPoints) { + // 重心に加わる力の合計を求める + Force3D f = new Force3D(0.0, 0.0, 0.0); + for (int n = 0; n < forces.size(); n++) { + f.add(forces.get(n)); + } + + // モーメントの合計を計算する + Position3D gc = getGravityCenter(); + Vector3d moment = new Vector3d(0.0, 0.0, 0.0); + for (int n2 = 0; n2 < forces.size(); n2++) { + moment.add(PhysicsUtility.calcMoment(forces.get(n2), gc, appPoints.get(n2))); + } + moveSub(interval, f, moment); + } + + private void moveSub(long interval, Force3D f, Vector3d moment) { + // 1.重心の運動方程式(ニュートン方程式) + // 加速度、速度計算 + Vector3d deltaV = f.getVector3d(); // 力ベクトルの取得 + deltaV.scale(1.0 / mass * ((double) interval / 1000.0)); // 加速度から速度の差分を計算 + Velocity3D v = getVelocity().add(deltaV); // 速度に差分を加算 + apply(v, false); + + // 重心位置計算 + Vector3d deltaP = velocity.getVector3d(); // 速度ベクトルの取得 + deltaP.scale(((double) interval / 1000.0)); + Position3D p = getPosition3D().add(deltaP); // 位置に差分を加算 + apply(p, false); + + // 2.オイラーの角運動方程式 + + // 角加速度、角速度計算 + AngularVelocity3D w = getAngularVelocity(); + Vector3d deltaAngularV = new Vector3d( + (moment.x + (inertia.iyy - inertia.izz) * w.getY() * w.getZ()) / inertia.ixx, + (moment.y + (inertia.izz - inertia.ixx) * w.getZ() * w.getX()) / inertia.iyy, + (moment.z + (inertia.ixx - inertia.iyy) * w.getX() * w.getY()) / inertia.izz); + deltaAngularV.scale((double) interval / 1000.0); + w.add(deltaAngularV); + apply(w, false); + + // 角速度による回転計算 + AxisAngle4d axisAngle = w.getAxisAngle4d(); + axisAngle.angle *= ((double) interval / 1000.0); + Quaternion3D q = getQuaternion().add(axisAngle); + apply(q, false); + } + + // 複製を作る + public Object3D duplicate() { + Object3D copy = new Solid3D(this); + return copy; + } + + public void scale(double s) { + super.scale(s); + inertia = new Inertia3D(this); + } + + public void scale(double sx, double sy, double sz) { + super.scale(sx, sy, sz); + inertia = new Inertia3D(this); + } + + public Velocity3D getVelocity() { + return (Velocity3D) velocity.clone(); + } + + public AngularVelocity3D getAngularVelocity() { + return (AngularVelocity3D) angularvelocity.clone(); + } + + // Velocity3D の applyTo 以外からは呼ばないこと + void setVelocity(Velocity3D v) { + velocity = (Velocity3D) v.clone(); + } + + // AngularVelocity3D の applyTo 以外からは呼ばないこと + void setAngularVelocity(AngularVelocity3D w) { + angularvelocity = (AngularVelocity3D) w.clone(); + } + + public void setGravityCenter(Position3D gravityCenter) { + this.gravityCenter = gravityCenter; + } + + public Position3D getGravityCenter() { + return getPosition3D().add(gravityCenter); + } + + public void setMass(double mass) { + this.mass = mass; + inertia = new Inertia3D(this); + } + + public double getMass() { + return mass; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Velocity3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Velocity3D.java new file mode 100644 index 0000000..4b9801a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/physics/Velocity3D.java @@ -0,0 +1,140 @@ +package com.google.ar.core.examples.java.common.framework.physics; + +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Property3D; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + + +public class Velocity3D extends Property3D { + private double x; + private double y; + private double z; + + public Velocity3D(Velocity3D v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Velocity3D(Vector3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + public Velocity3D(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Velocity3D() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + public void applyTo(Object3D o) { + ((Solid3D)o).setVelocity(this); + } + + public double getX() { + return this.x; + } + + public double getY() { + return this.y; + } + + public double getZ() { + return this.z; + } + + public Velocity3D setX(double x) { + this.x = x; + return this; + } + + public Velocity3D setY(double y) { + this.y = y; + return this; + } + + public Velocity3D setZ(double z) { + this.z = z; + return this; + } + + public Velocity3D add(double x, double y, double z){ + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public Velocity3D add(Vector3d v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + public Velocity3D setVector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + return this; + } + + public Vector3d getVector3d() { + return new Vector3d(x,y,z); + } + + @Override + public Property3D clone() { + return new Velocity3D(this); + } + + public Velocity3D rotX(double a) { + Vector3d v = getVector3d(); + Transform3D rotX = new Transform3D(); + rotX.rotX(a); + rotX.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D rotY(double a) { + Vector3d v = getVector3d(); + Transform3D rotY = new Transform3D(); + rotY.rotY(a); + rotY.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D rotZ(double a) { + Vector3d v = getVector3d(); + Transform3D rotZ = new Transform3D(); + rotZ.rotZ(a); + rotZ.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D setVelocity(double velocity) { + Vector3d v = getVector3d(); + double oldV = v.length(); + v.scale(velocity / oldV); + setVector3d(v); + return this; + } + + public Velocity3D mul(double d) { + this.x *= d; + this.y *= d; + this.z *= d; + return this; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/Event.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/Event.java new file mode 100644 index 0000000..65fef93 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/Event.java @@ -0,0 +1,13 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +public class Event { + private String name; + + public Event(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/FSM.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/FSM.java new file mode 100644 index 0000000..27cc9b8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/FSM.java @@ -0,0 +1,38 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; + +public class FSM { + private State initialState = null; + protected State currentState = null; + private Hashtable states = new Hashtable(); + + public FSM(State initialState, Hashtable states) { + this.initialState = initialState; + this.states = states; + currentState = initialState; + Collection allStates = states.values(); + Iterator it = allStates.iterator(); + while (it.hasNext()) { + State s = it.next(); + s.setOwner(this); + } + } + + public void addState(String stateName, State s) { + states.put(stateName, s); + s.setOwner(this); + } + + public boolean trans(Event e) { + currentState = currentState.getSuccessor(e); + if (currentState == null) return false; + return true; + } + + public State getCurrentState() { + return currentState; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/IWorld.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/IWorld.java new file mode 100644 index 0000000..21ca3cb --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/IWorld.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +public interface IWorld { + abstract public void dialogOpen(); + abstract public void dialogClose(); + abstract public void dialogMessage(String message); + abstract public void showOption(int n, String option); + abstract public boolean isDialogOpen(); + abstract public void action(String action, Event event, ScenarioState nextState); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioAction.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioAction.java new file mode 100644 index 0000000..106ae77 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioAction.java @@ -0,0 +1,13 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +public class ScenarioAction { + private String name; + + public ScenarioAction(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioFSM.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioFSM.java new file mode 100644 index 0000000..9909708 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioFSM.java @@ -0,0 +1,21 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +import java.util.Hashtable; + +public class ScenarioFSM extends FSM { + ScenarioManager manager; + + public ScenarioFSM(State initialState, Hashtable states, ScenarioManager manager) { + super(initialState, states); + this.manager = manager; + } + + public boolean trans(Event e) { + e = ((ScenarioState)currentState).canTrans(e); + if (e == null) return false; + ScenarioAction action = ((ScenarioState)currentState).getAction(e); + boolean result = super.trans(e); + manager.action(action, e, (ScenarioState)currentState); + return result; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioManager.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioManager.java new file mode 100644 index 0000000..823ffed --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioManager.java @@ -0,0 +1,183 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.helpers.DefaultHandler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +public class ScenarioManager { + private IWorld realWorld; + private Hashtable stateMachines = new Hashtable(); + private Hashtable allStates = new Hashtable(); + private Hashtable allEvents = new Hashtable(); + + public ScenarioManager(String xmlFileName, IWorld realWorld) { + this.realWorld = realWorld; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + factory.setValidating(true); + factory.setAttribute( + "http://java.sun.com/xml/jaxp/properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.setErrorHandler(new DefaultHandler()); + Document document = builder.parse(xmlFileName); + + // 有限状態マシンの作成、状態の登録、イベントの登録 + Hashtable allTrans = new Hashtable(); + NodeList scenario = document.getChildNodes(); + NodeList fsmNodes = scenario.item(0).getChildNodes(); + for (int n = 0; n < fsmNodes.getLength(); n++) { + Node fsmNode = fsmNodes.item(n); + if (fsmNode.getNodeName().equals("FSM")) { + Hashtable fsmStates = new Hashtable(); + NodeList stateNodes = fsmNode.getChildNodes(); + for (int m = 0; m < stateNodes.getLength(); m++) { + Node stateNode = stateNodes.item(m); + if (stateNode.getNodeName().equals("State")) { + ScenarioState state = new ScenarioState(); + String stateName = stateNode.getAttributes().getNamedItem("name").getNodeValue(); + fsmStates.put(stateName, state); + allStates.put(stateName, state); + Node stateMessageNode = stateNode.getAttributes().getNamedItem("message"); + if (stateMessageNode != null) state.setMessage(stateMessageNode.getNodeValue()); + NodeList eventNodes = stateNode.getChildNodes(); + allTrans.put(state, eventNodes); + for (int l = 0; l < eventNodes.getLength(); l++) { + Node eventNode = eventNodes.item(l); + if (eventNode.getNodeName().equals("Event")) { + String eventName = eventNode.getAttributes().getNamedItem("name").getNodeValue(); + Event e = allEvents.get(eventName); + if (e == null) { + e = new Event(eventName); + allEvents.put(eventName, e); + } + } + } + } + } + String initialStateName = fsmNode.getAttributes().getNamedItem("initial").getNodeValue(); + State initialState = fsmStates.get(initialStateName); + ScenarioFSM fsm = new ScenarioFSM(initialState, fsmStates, this); + String fsmName = fsmNode.getAttributes().getNamedItem("name").getNodeValue(); + stateMachines.put(fsmName, fsm); + } + } + + // 状態遷移を設定する + Set> allTransEntries = allTrans.entrySet(); + Iterator> it = allTransEntries.iterator(); + while (it.hasNext()) { + Entry transEntry = it.next(); + ScenarioState state = transEntry.getKey(); + NodeList eventNodes = transEntry.getValue(); + for (int l = 0; l < eventNodes.getLength(); l++) { + Node eventNode = eventNodes.item(l); + if (eventNode.getNodeName().equals("Event")) { + Node eventNameNode = eventNode.getAttributes().getNamedItem("name"); + Event e = null; + if (eventNameNode != null) { + String eventName = eventNameNode.getNodeValue(); + e = allEvents.get(eventName); + } + Node nextStateNameNode = eventNode.getAttributes().getNamedItem("trans"); + State nextState = null; + if (nextStateNameNode != null) { + String nextStateName = nextStateNameNode.getNodeValue(); + nextState = allStates.get(nextStateName); + } + Node syncEventNameNode = eventNode.getAttributes().getNamedItem("sync"); + Event syncEvent = null; + if (syncEventNameNode != null) { + String syncEventName = syncEventNameNode.getNodeValue(); + syncEvent = allEvents.get(syncEventName); + } + ArrayList guards = new ArrayList(); + Node guardStateNameNode = eventNode.getAttributes().getNamedItem("guard"); + if (guardStateNameNode != null) { + String guardStateName = guardStateNameNode.getNodeValue(); + State guardState = allStates.get(guardStateName); + if (guardState != null) guards.add(guardState); + } + Node guardStateNameNode2 = eventNode.getAttributes().getNamedItem("guard2"); + if (guardStateNameNode2 != null) { + String guardStateName2 = guardStateNameNode2.getNodeValue(); + State guardState2 = allStates.get(guardStateName2); + if (guardState2 != null) guards.add(guardState2); + } + Node guardStateNameNode3 = eventNode.getAttributes().getNamedItem("guard3"); + if (guardStateNameNode3 != null) { + String guardStateName3 = guardStateNameNode3.getNodeValue(); + State guardState3 = allStates.get(guardStateName3); + if (guardState3 != null) guards.add(guardState3); + } + Node actionNameNode = eventNode.getAttributes().getNamedItem("action"); + ScenarioAction action = null; + if (actionNameNode != null) { + String actionName = actionNameNode.getNodeValue(); + action = new ScenarioAction(actionName); + } + state.addTransition(e, nextState, syncEvent, guards, action); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void fire(Event e){ + Collection fsms = stateMachines.values(); + Iterator it = fsms.iterator(); + while (it.hasNext()) { + ScenarioFSM fsm = it.next(); + fsm.trans(e); + } + } + + public void fire(String eventName) { + Event e = allEvents.get(eventName); + if (e == null) return; + fire(e); + } + + public void action(ScenarioAction action, Event event, ScenarioState nextState) { + if (action != null) { + String sAction = action.getName(); + if (sAction.equals("openDialog")) { + realWorld.dialogOpen(); + } else if (sAction.equals("closeDialog")) { + realWorld.dialogClose(); + } else if (sAction.equals("print")) { + System.out.println(event.getName()); + } + realWorld.action(sAction, event, nextState); + } + if (realWorld.isDialogOpen()) { + String message = nextState.getMessage(); + if (message != null) { + realWorld.dialogMessage(message); + Enumeration events = nextState.getEvents(); + int n = 0; + while (events.hasMoreElements()) { + Event ev = events.nextElement(); + realWorld.showOption(n, ev.getName()); + n++; + } + } + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioState.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioState.java new file mode 100644 index 0000000..67794bc --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/ScenarioState.java @@ -0,0 +1,67 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; + +public class ScenarioState extends State { + private Hashtable transitionSyncs = new Hashtable(); + private Hashtable> transitionGuards = new Hashtable>(); + private Hashtable transitionActions = new Hashtable(); + private String message = null; + + public ScenarioState() { + super(); + } + + public ScenarioState(Hashtable transitions, + Hashtable transitionSyncs, + Hashtable> transitionGuards, + Hashtable transitionActions, + String message) { + super(transitions); + this.transitionSyncs = transitionSyncs; + this.transitionGuards = transitionGuards; + this.transitionActions = transitionActions; + this.message = message; + } + + public void addTransition(Event event, State succ, Event syncEvent, ArrayList guards, ScenarioAction action) { + addTransition(event, succ); + if (syncEvent != null) transitionSyncs.put(event, syncEvent); + if (guards != null) transitionGuards.put(event, guards); + if (action != null) transitionActions.put(event, action); + } + + Event canTrans(Event event) { + Enumeration events = getEvents(); + while (events.hasMoreElements()) { + Event e = events.nextElement(); + if (event == e || event == transitionSyncs.get(e)) { + ArrayList guardStates = transitionGuards.get(e); + boolean bSatisfyGuard = true; + for (int n = 0; n < guardStates.size(); n++) { + State s = guardStates.get(n); + if (s.getOwner().getCurrentState() != s) { + bSatisfyGuard = false; + break; + } + } + if (bSatisfyGuard) return e; + } + } + return null; + } + + public ScenarioAction getAction(Event e) { + return transitionActions.get(e); + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/State.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/State.java new file mode 100644 index 0000000..990833c --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/scenario/State.java @@ -0,0 +1,37 @@ +package com.google.ar.core.examples.java.common.framework.scenario; + +import java.util.Enumeration; +import java.util.Hashtable; + +public class State { + private FSM owner = null; + private Hashtable transitions = new Hashtable(); + + public State() { + } + + public State(Hashtable transitions) { + this.transitions = transitions; + } + + public void addTransition(Event e, State s) { + transitions.put(e, s); + } + + public Enumeration getEvents() { + return transitions.keys(); + } + + public State getSuccessor(Event e) { + if (!transitions.containsKey(e)) return null; + return transitions.get(e); + } + + public void setOwner(FSM fsm) { + owner = fsm; + } + + public FSM getOwner() { + return owner; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Camera3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Camera3D.java new file mode 100644 index 0000000..b4bbf69 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Camera3D.java @@ -0,0 +1,459 @@ +package com.google.ar.core.examples.java.common.framework.view3D; + +import com.google.ar.core.examples.java.common.framework.model3D.GeometryUtility; +import com.google.ar.core.examples.java.common.framework.model3D.Object3D; +import com.google.ar.core.examples.java.common.framework.model3D.Placeable; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.framework.model3D.Universe; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.TransformGroup; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +/** + * 画角調整機能が付いたカメラ
+ * 視点、注視対象、視線のうち2つを指定して使う。 + * @author 新田直也 + * + */ +public class Camera3D { + private static final double NEAREST = 3.0; + private static final Vector3d VIEW_FORWARD = new Vector3d(0.0, 0.0, -1.0); + private static final Vector3d VIEW_DOWN = new Vector3d(0.0, -1.0, 0.0); + + private Universe universe = null; + + private double frontClipDistance = 0.5; // デプスバッファが小さいためあまり小さい値にできない + private double backClipDistance = 1000.0; // デプスバッファが小さいためあまり大きい値にできない + private double fieldOfView = Math.PI / 2.0; + + private TransformGroup viewPlatformTransform = null; + protected ArrayList targetObjList = null; + protected ArrayList targetList = null; + protected Position3D viewPoint = null; + protected Object3D viewPointObj = null; + private Vector3d viewLine = null; + private Vector3d cameraBack = null; + private Vector3d viewUp = null; + private boolean fParallel = false; + + // 以下の変数は省メモリ化のため導入 + private Transform3D worldToView = new Transform3D(); + private double matrix[] = new double[]{1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0}; + + private Transform3D cameraTransform = new Transform3D(); + + public Camera3D(Universe universe) { + this.universe = universe; + } + + public Universe getUniverse() { + return universe; + } + + /** + * カメラの注視点を追加する + * + * @param target + * 注視点 + */ + public void addTarget(Position3D target) { + if (targetList == null) targetList = new ArrayList(); + targetList.add(target); + } + + /** + * カメラの注視対象を追加する + * + * @param target + * 注視対象 + */ + public void addTarget(Object3D target) { + if (targetObjList == null) targetObjList = new ArrayList(); + targetObjList.add(target); + } + + /** + * カメラの注視対象を追加する + * + * @param target + * 注視対象 + */ + public void addTarget(Placeable target) { + if (targetObjList == null) targetObjList = new ArrayList(); + if (target.getBody() instanceof Object3D) { + targetObjList.add((Object3D)target.getBody()); + } + } + + /** + * カメラの視点を設定する + * + * @param viewPoint + * 視点 + */ + public void setViewPoint(Position3D viewPoint) { + this.viewPoint = viewPoint; + } + + /** + * カメラの視点を設定する + * + * @param viewPoint + * 視点となるオブジェクト + */ + public void setViewPoint(Object3D viewPointObj) { + this.viewPointObj = viewPointObj; + } + + /** + * カメラの視点を設定する + * + * @param viewPoint + * 視点となる登場物 + */ + public void setViewPoint(Placeable viewPointActor) { + if (viewPointActor.getBody() instanceof Object3D) { + viewPointObj = (Object3D)viewPointActor.getBody(); + } + } + + /** + * 手前からの視線に設定する + */ + public void setSideView() { + viewLine = VIEW_FORWARD; + } + + /** + * 上から見下ろした視線に設定する + */ + public void setTopView() { + viewLine = VIEW_DOWN; + } + + /** + * 視線を設定する + */ + public void setViewLine(Vector3d viewLine) { + this.viewLine = viewLine; + } + + + /** + * 視野角を設定する + * @param a 視野角 + */ + public void setFieldOfView(double a) { + fieldOfView = a; + } + + /** + * 視野角を取得する + * @return 視野角 + */ + public double getFieldOfView() { + return fieldOfView; + } + + /** + * フロントクリップ距離を設定する + * @param d + */ + public void setFrontClipDistance(double d) { + frontClipDistance = d; + } + + /** + * フロントクリップ距離を取得する + * @return フロントクリップ距離 + */ + public double getFrontClipDistance() { + return frontClipDistance; + } + + /** + * バッククリップ距離を設定する + * @param d + */ + public void setBackClipDistance(double d) { + backClipDistance = d; + } + + /** + * バッククリップ距離を取得する + * @return バッククリップ距離 + */ + public double getBackClipDistance() { + return backClipDistance; + } + + public void setViewUp(Vector3d viewUp) { + this.viewUp = viewUp; + } + + public Position3D getViewPoint() { + if (viewPoint != null) return viewPoint; + if (viewPointObj != null) return viewPointObj.getPosition3D(); + // 視点が設定されていない場合 + Vector3d center = getTargetCenter(); + if (center != null) { + Vector3d vz = new Vector3d(); + if (viewLine != null) { + // 注視対象と視線が設定されている場合 + vz.negate(viewLine); + } else { + // 注視対象のみが設定されている場合 + vz.negate(VIEW_FORWARD); + } + Vector3d vx = new Vector3d(); + Vector3d vy = new Vector3d(); + vx.cross(vz, VIEW_DOWN); + if (vx.length() > GeometryUtility.TOLERANCE) { + vx.normalize(); + } else { + vx = new Vector3d(1.0, 0.0, 0.0); + } + vy.cross(vz, vx); + + // 注視対象から下がる距離 + if (cameraBack != null) { + vx.scale(cameraBack.x); + vy.scale(cameraBack.y); + vz.scale(cameraBack.z); + center.add(vz); + center.add(vy); + center.add(vz); + } else { + double z = getStandBackDistance(vx, vy); + vz.scale(z); + center.add(vz); + } + return new Position3D(center); + } + // 視点も注視点も設定されていない場合 + return new Position3D(); + } + + public Vector3d getViewLine() { + if (viewLine != null) { + // 視線が設定されている場合 + return viewLine; + } + Vector3d center = getTargetCenter(); + if (center != null) { + if (viewPoint != null) { + center.sub(viewPoint.getVector3d()); + } else if (viewPointObj != null) { + center.sub(viewPointObj.getPosition3D().getVector3d()); + } else { + center.set(VIEW_FORWARD); + } + } else { + center = new Vector3d(VIEW_FORWARD); + } + center.normalize(); + return center; + } + + public Vector3d getViewUp() { + if (viewUp != null) { + return viewUp; + } else { + Vector3d vy = new Vector3d(0.0, 1.0, 0.0); + Vector3d vv = (Vector3d) getViewLine().clone(); + vv.cross(vy, vv); + vv.cross(getViewLine(), vv); + return vv; + } + } + + public Vector3d getTargetCenter() { + Vector3d center = new Vector3d(); + if (targetObjList != null && targetObjList.size() != 0) { + for (int i = 0; i < targetObjList.size(); i++) { + Position3D position = targetObjList.get(i).getPosition3D(); + center.add(position.getVector3d()); + } + center.scale(1.0 / targetObjList.size()); + } else if (targetList != null && targetList.size() != 0) { + for (int i = 0; i < targetList.size(); i++) { + Position3D position = targetList.get(i); + center.add(position.getVector3d()); + } + center.scale(1.0 / targetList.size()); + } else { + return null; + } + return center; + } + + /** + * 注視対象や視点の移動、視線の変化に伴う画角調整 + */ + public void adjust(long interval) { + // カメラ座標系(vx, vy, vz)を計算する + Vector3d vx = new Vector3d(), vy = new Vector3d(), vz = new Vector3d(); + if (viewLine == null) { + // 視線が設定されていない場合 + if ((viewPoint == null && viewPointObj == null) + || ((targetObjList == null || targetObjList.size() == 0) + && (targetList == null || targetList.size() == 0))) { + // 視点または注視対象が設定されていない場合、手前からの視線にする + vz.negate(VIEW_FORWARD); + } else { + // 注視対象の重心を注視点とする + Vector3d center = getTargetCenter(); + if (center == null) center = new Vector3d(); + if (viewPoint != null) { + center.sub(viewPoint.getVector3d()); + } else { + center.sub(viewPointObj.getPosition3D().getVector3d()); + } + center.normalize(); + vz.negate(center); + } + } else { + // 視線が設定されている場合 vz を視線方向 と逆向きに設定する + viewLine.normalize(); + vz.negate(viewLine); + } + vx.cross(vz, VIEW_DOWN); + if (vx.length() > GeometryUtility.TOLERANCE) { + vx.normalize(); + } else { + vx = new Vector3d(1.0, 0.0, 0.0); + } + vy.cross(vz, vx); + + // 世界座標からカメラ座標への変換を計算する + if (viewPoint != null || viewPointObj != null) { + // 視点が設定されている場合 + Vector3d vp; + if (viewPoint != null) { + vp = viewPoint.getVector3d(); + } else { + vp = viewPointObj.getPosition3D().getVector3d(); + } + matrix[0] = vx.x; matrix[1] = vx.y; matrix[2] = vx.z; matrix[3] = 0.0; + matrix[4] = vy.x; matrix[5] = vy.y; matrix[6] = vy.z; matrix[7] = 0.0; + matrix[8] = vz.x; matrix[9] = vz.y; matrix[10] = vz.z; matrix[11] = 0.0; + matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = 0.0; matrix[15] = 1.0; + worldToView.set(matrix); + worldToView.invert(); + worldToView.setTranslation(vp); + } else { + // 視点が設定されていない場合、注視対象と視線から(カメラ座標系上での)視点を逆計算する + if ((targetObjList == null || targetObjList.size() == 0) + && (targetList == null || targetList.size() == 0)) return; // 視点も注視対象も設定されていない + + // 注視対象の中心 + Vector3d center = getTargetCenter(); + + // カメラ座標上での注視点の座標 + Vector3d eye = new Vector3d(center.dot(vx), center.dot(vy), center.dot(vz)); + if (cameraBack != null) { + // カメラの引き + eye.add(cameraBack); + } else { + // 注視対象が入るようにカメラを引く + double z = getStandBackDistance(vx, vy); + eye.z += z; + } + + matrix[0] = vx.x; matrix[1] = vx.y; matrix[2] = vx.z; matrix[3] = -eye.x; + matrix[4] = vy.x; matrix[5] = vy.y; matrix[6] = vy.z; matrix[7] = -eye.y; + matrix[8] = vz.x; matrix[9] = vz.y; matrix[10] = vz.z; matrix[11] = -eye.z; + matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = 0.0; matrix[15] = 1.0; + worldToView.set(matrix); + worldToView.invert(); + } + if (viewPlatformTransform != null) viewPlatformTransform.setTransform(worldToView); + } + + public double getStandBackDistance(Vector3d vx, Vector3d vy) { + double xmax = 0; + double xmin = 0; + double ymax = 0; + double ymin = 0; + if (targetObjList != null && targetObjList.size() != 0) { + for (int i = 0; i < targetObjList.size(); i++) { + Position3D position = targetObjList.get(i).getPosition3D(); + double px = position.getVector3d().dot(vx); + double py = position.getVector3d().dot(vy); + if (i == 0) { + xmax = xmin = px; + ymax = ymin = py; + } else { + if (xmax < px) + xmax = px; + if (xmin > px) + xmin = px; + if (ymax < py) + ymax = py; + if (ymin > py) + ymin = py; + } + } + } else if (targetList != null && targetList.size() != 0) { + for (int i = 0; i < targetList.size(); i++) { + Position3D position = targetList.get(i); + double px = position.getVector3d().dot(vx); + double py = position.getVector3d().dot(vy); + if (i == 0) { + xmax = xmin = px; + ymax = ymin = py; + } else { + if (xmax < px) + xmax = px; + if (xmin > px) + xmin = px; + if (ymax < py) + ymax = py; + if (ymin > py) + ymin = py; + } + } + } + + double x = (xmax + xmin) / 2; + double y = (ymax + ymin) / 2; + double x_diff = Math.abs(xmax - x); + double y_diff = Math.abs(ymax - y); + if (x_diff < NEAREST) + x_diff = NEAREST; + if (y_diff < NEAREST) + y_diff = NEAREST; + double z; + if (x_diff < y_diff) { + z = y_diff / Math.tan(Math.PI / 18.0); + } else { + z = x_diff / Math.tan(Math.PI / 18.0); + } + return z; + } + + public void setWorldToView(Position3D vp, Vector3d vl) { + setViewPoint(vp); + setViewLine(vl); + //上方向のベクトル計算 + Vector3d vy = new Vector3d(0, 1, 0); + Vector3d vv = (Vector3d) vl.clone(); + vv.cross(vy, vv); + vv.cross(vv, vl); +// activity.setCamera(vp, vl, vv); + } + + public void setParallel(){ + this.fParallel = true; + } + + public boolean isParallel(){ + return this.fParallel; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Viewer3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Viewer3D.java new file mode 100644 index 0000000..56eb70b --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/framework/view3D/Viewer3D.java @@ -0,0 +1,97 @@ +package com.google.ar.core.examples.java.common.framework.view3D; + +import com.google.ar.core.examples.java.common.framework.model3D.BackgroundBox; +import com.google.ar.core.examples.java.common.framework.model3D.IViewer3D; +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; +import com.google.ar.core.examples.java.common.java3d.Box; +import com.google.ar.core.examples.java.common.java3d.Cone; +import com.google.ar.core.examples.java.common.java3d.Cylinder; +import com.google.ar.core.examples.java.common.java3d.GraphicsContext3D; +import com.google.ar.core.examples.java.common.java3d.Light; +import com.google.ar.core.examples.java.common.java3d.Node; +import com.google.ar.core.examples.java.common.java3d.Shape3D; +import com.google.ar.core.examples.java.common.java3d.Sphere; +import com.google.ar.core.examples.java.common.java3d.Transform3D; +import com.google.ar.core.examples.java.common.java3d.Vector3d; + +import java.util.ArrayList; + +public class Viewer3D implements IViewer3D { + private GraphicsContext3D gc3D = null; + private ArrayList lights = null; + private BackgroundBox skyBox = null; + private Camera3D camera = null; + + public Viewer3D(Camera3D camera) { + this.camera = camera; + } + + @Override + public void setGraphicsContext3D(GraphicsContext3D gc3D) { + if (this.gc3D != gc3D) { + this.gc3D = gc3D; + } + } + + @Override + public void surfaceChanged(int width, int height) { + gc3D.update(width, height, (float)camera.getFieldOfView(), (float)camera.getFrontClipDistance(), (float)camera.getBackClipDistance(), camera.isParallel()); + } + + @Override + public void onDrawFrame() { + Position3D eye = camera.getViewPoint(); + Position3D center = eye.clone().add(camera.getViewLine()); + Vector3d up = camera.getViewUp(); + gc3D.update((float)camera.getFieldOfView(), (float)camera.getFrontClipDistance(), (float)camera.getBackClipDistance(), eye, center, up, camera.isParallel()); + } + + @Override + public void update(ArrayList lights, BackgroundBox skyBox) { + // 光源の更新 + if (this.lights != lights) { + this.lights = lights; + } + + // スカイボックスの更新 + if (this.skyBox != skyBox) { + this.skyBox = skyBox; + } + } + + @Override + public void draw(Node node) { + if (node instanceof Box) { + gc3D.draw(((Box)node).getShape(Box.FRONT)); + gc3D.draw(((Box)node).getShape(Box.BACK)); + gc3D.draw(((Box)node).getShape(Box.LEFT)); + gc3D.draw(((Box)node).getShape(Box.RIGHT)); + gc3D.draw(((Box)node).getShape(Box.TOP)); + gc3D.draw(((Box)node).getShape(Box.BOTTOM)); + } else if (node instanceof Cone) { + gc3D.draw(((Cone)node).getShape(Cone.BODY)); + gc3D.draw(((Cone)node).getShape(Cone.CAP)); + } else if (node instanceof Cylinder) { + gc3D.draw(((Cylinder)node).getShape(Cylinder.BODY)); + gc3D.draw(((Cylinder)node).getShape(Cylinder.TOP)); + gc3D.draw(((Cylinder)node).getShape(Cylinder.BOTTOM)); + } else if (node instanceof Sphere) { + gc3D.draw(((Sphere)node).getShape(Sphere.BODY)); + } else if (node instanceof Shape3D) { + gc3D.draw((Shape3D)node); + } else if (node instanceof Light){ + gc3D.updateLightState((Light)node); + } + } + + @Override + public void pushTransform(Transform3D transform) { + gc3D.pushMatrix(); + gc3D.multMatrix(transform); + } + + @Override + public void popTransform() { + gc3D.popMatrix(); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/CameraPermissionHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/CameraPermissionHelper.java new file mode 100644 index 0000000..7617e36 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/CameraPermissionHelper.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.provider.Settings; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +/** Helper to ask camera permission. */ +public final class CameraPermissionHelper { + private static final int CAMERA_PERMISSION_CODE = 0; + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + + /** Check to see we have the necessary permissions for this app. */ + public static boolean hasCameraPermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION) + == PackageManager.PERMISSION_GRANTED; + } + + /** Check to see we have the necessary permissions for this app, and ask for them if we don't. */ + public static void requestCameraPermission(Activity activity) { + ActivityCompat.requestPermissions( + activity, new String[] {CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE); + } + + /** Check to see if we need to show the rationale for this permission. */ + public static boolean shouldShowRequestPermissionRationale(Activity activity) { + return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION); + } + + /** Launch Application Setting to grant permission. */ + public static void launchPermissionSettings(Activity activity) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", activity.getPackageName(), null)); + activity.startActivity(intent); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/DisplayRotationHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/DisplayRotationHelper.java new file mode 100644 index 0000000..e79434b --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/DisplayRotationHelper.java @@ -0,0 +1,164 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.app.Activity; +import android.content.Context; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.view.Display; +import android.view.Surface; +import android.view.WindowManager; +import com.google.ar.core.Session; + +/** + * Helper to track the display rotations. In particular, the 180 degree rotations are not notified + * by the onSurfaceChanged() callback, and thus they require listening to the android display + * events. + */ +public final class DisplayRotationHelper implements DisplayListener { + private boolean viewportChanged; + private int viewportWidth; + private int viewportHeight; + private final Display display; + private final DisplayManager displayManager; + private final CameraManager cameraManager; + + /** + * Constructs the DisplayRotationHelper but does not register the listener yet. + * + * @param context the Android {@link Context}. + */ + public DisplayRotationHelper(Context context) { + displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); + cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + display = windowManager.getDefaultDisplay(); + } + + /** Registers the display listener. Should be called from {@link Activity#onResume()}. */ + public void onResume() { + displayManager.registerDisplayListener(this, null); + } + + /** Unregisters the display listener. Should be called from {@link Activity#onPause()}. */ + public void onPause() { + displayManager.unregisterDisplayListener(this); + } + + /** + * Records a change in surface dimensions. This will be later used by {@link + * #updateSessionIfNeeded(Session)}. Should be called from {@link + * android.opengl.GLSurfaceView.Renderer + * #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}. + * + * @param width the updated width of the surface. + * @param height the updated height of the surface. + */ + public void onSurfaceChanged(int width, int height) { + viewportWidth = width; + viewportHeight = height; + viewportChanged = true; + } + + /** + * Updates the session display geometry if a change was posted either by {@link + * #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This + * function should be called explicitly before each call to {@link Session#update()}. This + * function will also clear the 'pending update' (viewportChanged) flag. + * + * @param session the {@link Session} object to update if display geometry changed. + */ + public void updateSessionIfNeeded(Session session) { + if (viewportChanged) { + int displayRotation = display.getRotation(); + session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight); + viewportChanged = false; + } + } + + /** + * Returns the aspect ratio of the GL surface viewport while accounting for the display rotation + * relative to the device camera sensor orientation. + */ + public float getCameraSensorRelativeViewportAspectRatio(String cameraId) { + float aspectRatio; + int cameraSensorToDisplayRotation = getCameraSensorToDisplayRotation(cameraId); + switch (cameraSensorToDisplayRotation) { + case 90: + case 270: + aspectRatio = (float) viewportHeight / (float) viewportWidth; + break; + case 0: + case 180: + aspectRatio = (float) viewportWidth / (float) viewportHeight; + break; + default: + throw new RuntimeException("Unhandled rotation: " + cameraSensorToDisplayRotation); + } + return aspectRatio; + } + + /** + * Returns the rotation of the back-facing camera with respect to the display. The value is one of + * 0, 90, 180, 270. + */ + public int getCameraSensorToDisplayRotation(String cameraId) { + CameraCharacteristics characteristics; + try { + characteristics = cameraManager.getCameraCharacteristics(cameraId); + } catch (CameraAccessException e) { + throw new RuntimeException("Unable to determine display orientation", e); + } + + // Camera sensor orientation. + int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + + // Current display orientation. + int displayOrientation = toDegrees(display.getRotation()); + + // Make sure we return 0, 90, 180, or 270 degrees. + return (sensorOrientation - displayOrientation + 360) % 360; + } + + private int toDegrees(int rotation) { + switch (rotation) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + default: + throw new RuntimeException("Unknown rotation " + rotation); + } + } + + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + viewportChanged = true; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/FullScreenHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/FullScreenHelper.java new file mode 100644 index 0000000..4178ba9 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/FullScreenHelper.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.app.Activity; +import android.view.View; + +/** Helper to set up the Android full screen mode. */ +public final class FullScreenHelper { + /** + * Sets the Android fullscreen flags. Expected to be called from {@link + * Activity#onWindowFocusChanged(boolean hasFocus)}. + * + * @param activity the Activity on which the full screen mode will be set. + * @param hasFocus the hasFocus flag passed from the {@link Activity#onWindowFocusChanged(boolean + * hasFocus)} callback. + */ + public static void setFullScreenOnWindowFocusChanged(Activity activity, boolean hasFocus) { + if (hasFocus) { + // https://developer.android.com/training/system-ui/immersive.html#sticky + activity + .getWindow() + .getDecorView() + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/SnackbarHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/SnackbarHelper.java new file mode 100644 index 0000000..e518c49 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/SnackbarHelper.java @@ -0,0 +1,124 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.app.Activity; +import android.support.design.widget.BaseTransientBottomBar; +import android.support.design.widget.Snackbar; +import android.view.View; +import android.widget.TextView; + +/** + * Helper to manage the sample snackbar. Hides the Android boilerplate code, and exposes simpler + * methods. + */ +public final class SnackbarHelper { + private static final int BACKGROUND_COLOR = 0xbf323232; + private Snackbar messageSnackbar; + private enum DismissBehavior { HIDE, SHOW, FINISH }; + private int maxLines = 2; + private String lastMessage = ""; + + public boolean isShowing() { + return messageSnackbar != null; + } + + /** Shows a snackbar with a given message. */ + public void showMessage(Activity activity, String message) { + if (!message.isEmpty() && (!isShowing() || !lastMessage.equals(message))) { + lastMessage = message; + show(activity, message, DismissBehavior.HIDE); + } + } + + /** Shows a snackbar with a given message, and a dismiss button. */ + public void showMessageWithDismiss(Activity activity, String message) { + show(activity, message, DismissBehavior.SHOW); + } + + /** + * Shows a snackbar with a given error message. When dismissed, will finish the activity. Useful + * for notifying errors, where no further interaction with the activity is possible. + */ + public void showError(Activity activity, String errorMessage) { + show(activity, errorMessage, DismissBehavior.FINISH); + } + + /** + * Hides the currently showing snackbar, if there is one. Safe to call from any thread. Safe to + * call even if snackbar is not shown. + */ + public void hide(Activity activity) { + if (!isShowing()) { + return; + } + lastMessage = ""; + Snackbar messageSnackbarToHide = messageSnackbar; + messageSnackbar = null; + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + messageSnackbarToHide.dismiss(); + } + }); + } + + public void setMaxLines(int lines) { + maxLines = lines; + } + + private void show( + final Activity activity, final String message, final DismissBehavior dismissBehavior) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + messageSnackbar = + Snackbar.make( + activity.findViewById(android.R.id.content), + message, + Snackbar.LENGTH_INDEFINITE); + messageSnackbar.getView().setBackgroundColor(BACKGROUND_COLOR); + if (dismissBehavior != DismissBehavior.HIDE) { + messageSnackbar.setAction( + "Dismiss", + new View.OnClickListener() { + @Override + public void onClick(View v) { + messageSnackbar.dismiss(); + } + }); + if (dismissBehavior == DismissBehavior.FINISH) { + messageSnackbar.addCallback( + new BaseTransientBottomBar.BaseCallback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + super.onDismissed(transientBottomBar, event); + activity.finish(); + } + }); + } + } + ((TextView) + messageSnackbar + .getView() + .findViewById(android.support.design.R.id.snackbar_text)) + .setMaxLines(maxLines); + messageSnackbar.show(); + } + }); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TapHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TapHelper.java new file mode 100644 index 0000000..e339f51 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TapHelper.java @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +/** + * Helper to detect taps using Android GestureDetector, and pass the taps between UI thread and + * render thread. + */ +public final class TapHelper implements OnTouchListener { + private final GestureDetector gestureDetector; + private final BlockingQueue queuedSingleTaps = new ArrayBlockingQueue<>(16); + + /** + * Creates the tap helper. + * + * @param context the application's context. + */ + public TapHelper(Context context) { + gestureDetector = + new GestureDetector( + context, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + // Queue tap if there is space. Tap is lost if queue is full. + queuedSingleTaps.offer(e); + return true; + } + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + }); + } + + /** + * Polls for a tap. + * + * @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued. + */ + public MotionEvent poll() { + return queuedSingleTaps.poll(); + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + return gestureDetector.onTouchEvent(motionEvent); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TrackingStateHelper.java b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TrackingStateHelper.java new file mode 100644 index 0000000..823b607 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/helpers/TrackingStateHelper.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.helpers; + +import android.app.Activity; +import android.view.WindowManager; +import com.google.ar.core.Camera; +import com.google.ar.core.TrackingFailureReason; +import com.google.ar.core.TrackingState; + +/** Gets human readibly tracking failure reasons and suggested actions. */ +public final class TrackingStateHelper { + private static final String INSUFFICIENT_FEATURES_MESSAGE = + "Can't find anything. Aim device at a surface with more texture or color."; + private static final String EXCESSIVE_MOTION_MESSAGE = "Moving too fast. Slow down."; + private static final String INSUFFICIENT_LIGHT_MESSAGE = + "Too dark. Try moving to a well-lit area."; + private static final String BAD_STATE_MESSAGE = + "Tracking lost due to bad internal state. Please try restarting the AR experience."; + + private final Activity activity; + + private TrackingState previousTrackingState; + + public TrackingStateHelper(Activity activity) { + this.activity = activity; + } + + /** Keep the screen unlocked while tracking, but allow it to lock when tracking stops. */ + public void updateKeepScreenOnFlag(TrackingState trackingState) { + if (trackingState == previousTrackingState) { + return; + } + + previousTrackingState = trackingState; + switch (trackingState) { + case PAUSED: + case STOPPED: + activity.runOnUiThread( + () -> activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); + break; + case TRACKING: + activity.runOnUiThread( + () -> activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); + break; + } + } + + public static String getTrackingFailureReasonString(Camera camera) { + TrackingFailureReason reason = camera.getTrackingFailureReason(); + switch (reason) { + case NONE: + return ""; + case BAD_STATE: + return BAD_STATE_MESSAGE; + case INSUFFICIENT_LIGHT: + return INSUFFICIENT_LIGHT_MESSAGE; + case EXCESSIVE_MOTION: + return EXCESSIVE_MOTION_MESSAGE; + case INSUFFICIENT_FEATURES: + return INSUFFICIENT_FEATURES_MESSAGE; + } + return "Unknown tracking failure reason: " + reason; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AmbientLight.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AmbientLight.java new file mode 100644 index 0000000..5055ec6 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AmbientLight.java @@ -0,0 +1,13 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class AmbientLight extends Light { + + @Override + public Node cloneTree() { + return new AmbientLight(color); + } + + public AmbientLight(Color3f c){ + super(c); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Appearance.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Appearance.java new file mode 100644 index 0000000..272795f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Appearance.java @@ -0,0 +1,77 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Appearance extends NodeComponent { + private Material material = null; + private Texture texture = null; + private TextureAttributes textureAttribute = null; + private TexCoordGeneration texCoordGeneration = null; + private TextureUnitState[] stateArray; + + public void setMaterial(Material m) { + this.material = m; + } + + public Material getMaterial() { + return material; + } + + public void setTexture(Texture texture) { + this.texture = texture; + } + + public Texture getTexture() { + return texture; + } + + public int getTextureUnitCount() { + if (stateArray == null) return 0; + return stateArray.length; + } + + public TextureUnitState getTextureUnitState(int textureUnit) { + if (stateArray == null) return null; + return stateArray[textureUnit]; + } + + public void setTextureUnitState(TextureUnitState[] stateArray) { + this.stateArray = stateArray; + } + + public void setTextureUnitState(int textureUnit, TextureUnitState textureUnitState) { + stateArray[textureUnit] = textureUnitState; + } + + public TextureAttributes getTextureAttributes() { + return textureAttribute; + } + + public void setTextureAttributes(TextureAttributes textureAttribute) { + this.textureAttribute = textureAttribute; + } + + public TexCoordGeneration getTexCoordGeneration() { + return texCoordGeneration; + } + + public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) { + this.texCoordGeneration = texCoordGeneration; + } + + @Override + public NodeComponent cloneNodeComponent() { + Appearance ap = new Appearance(); + if (material != null) { + ap.material = (Material)material.cloneNodeComponent(); + } + if (texture != null) { + ap.texture = (Texture)texture.cloneNodeComponent(); + } + if (textureAttribute != null) { + ap.textureAttribute = (TextureAttributes)textureAttribute.cloneNodeComponent(); + } + if (texCoordGeneration != null) { + ap.texCoordGeneration = (TexCoordGeneration)texCoordGeneration.cloneNodeComponent(); + } + return ap; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AxisAngle4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AxisAngle4d.java new file mode 100644 index 0000000..ae2a6f3 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/AxisAngle4d.java @@ -0,0 +1,62 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class AxisAngle4d { + public double angle; + public double x; + public double y; + public double z; + static final double EPS = 1.0e-12; + + // コンストラクタ + public AxisAngle4d() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public AxisAngle4d(double px, double py, double pz, double pa) { + x = px; + y = py; + z = pz; + angle = pa; + } + + public AxisAngle4d(AxisAngle4d a) { + x = a.x; + y = a.y; + z = a.z; + angle = a.angle; + } + + public AxisAngle4d(Vector3d axis, double angle) { + x = axis.x; + y = axis.y; + z = axis.z; + this.angle = angle; + } + + public final void set(Quat4d q1) { + double mag = q1.x*q1.x + q1.y*q1.y + q1.z*q1.z; + + if( mag > EPS ) { + mag = Math.sqrt(mag); + double invMag = 1.0/mag; + + x = q1.x*invMag; + y = q1.y*invMag; + z = q1.z*invMag; + angle = 2.0* Math.atan2(mag, q1.w); + } else { + x = 0.0d; + y = 1.0d; + z = 0.0d; + angle = 0.0d; + } + } + + public AxisAngle4d clone() { + return new AxisAngle4d(x, y, z, angle); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Background.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Background.java new file mode 100644 index 0000000..b541df9 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Background.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Background extends Leaf { + + @Override + public Node cloneTree() { + return new Background(); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingBox.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingBox.java new file mode 100644 index 0000000..037071f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingBox.java @@ -0,0 +1,37 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class BoundingBox extends Bounds { + Point3d lower; + Point3d upper; + + public BoundingBox(Point3d lower, Point3d upper) { + boundId = BOUNDING_BOX; + this.lower = lower; + this.upper = upper; + } + + @Override + public Object clone() { + // TODO Auto-generated method stub + return new BoundingBox(new Point3d(lower), new Point3d(upper)); + } + + @Override + public void combine(Bounds boundsObject) { + // TODO Auto-generated method stub + + } + + @Override + public void transform(Transform3D trans) { + // TODO Auto-generated method stub + + } + + @Override + public boolean intersect(Bounds boundsObject) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingPolytope.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingPolytope.java new file mode 100644 index 0000000..db1557f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingPolytope.java @@ -0,0 +1,543 @@ +package com.google.ar.core.examples.java.common.java3d; + + +public class BoundingPolytope extends Bounds { + Vector4d[] planes; + double[] mag; // magnitude of plane vector + double[] pDotN; // point on plane dotted with normal + Point3d[] verts; // vertices of polytope + int nVerts; // number of verts in polytope + Point3d centroid = new Point3d(); // centroid of polytope + Point3d boxVerts[]; + boolean allocBoxVerts = false; + + /** + * Constructs a BoundingPolytope using the specified planes. + * + * @param planes + * a set of planes defining the polytope. + * @exception IllegalArgumentException + * if the length of the specified array of planes is less + * than 4. + */ + public BoundingPolytope(Vector4d[] planes) { + boundId = BOUNDING_POLYTOPE; + int i; + double invMag; + this.planes = new Vector4d[planes.length]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + computeAllVerts(); // XXXX: lazy evaluate + } + + /** + * Constructs a BoundingPolytope and initializes it to a set of 6 planes + * that defines a cube such that -1 <= x,y,z <= 1. The values of the planes + * are as follows: + *
    + * planes[0] : x <= 1 (1,0,0,-1)
    + * planes[1] : -x <= 1 (-1,0,0,-1)
    + * planes[2] : y <= 1 (0,1,0,-1)
    + * planes[3] : -y <= 1 (0,-1,0,-1)
    + * planes[4] : z <= 1 (0,0,1,-1)
    + * planes[5] : -z <= 1 (0,0,-1,-1)
    + *
+ */ + public BoundingPolytope() { + boundId = BOUNDING_POLYTOPE; + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + planes[0] = new Vector4d(1.0, 0.0, 0.0, -1.0); + planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0); + planes[2] = new Vector4d(0.0, 1.0, 0.0, -1.0); + planes[3] = new Vector4d(0.0, -1.0, 0.0, -1.0); + planes[4] = new Vector4d(0.0, 0.0, 1.0, -1.0); + planes[5] = new Vector4d(0.0, 0.0, -1.0, -1.0); + mag[0] = 1.0; + mag[1] = 1.0; + mag[2] = 1.0; + mag[3] = 1.0; + mag[4] = 1.0; + mag[5] = 1.0; + computeAllVerts(); // XXXX: lazy evaluate + } + + public void setPlanes(Vector4d[] planes) { + int i; + double invMag; + this.planes = new Vector4d[planes.length]; + pDotN = new double[planes.length]; + mag = new double[planes.length]; + if (planes.length <= 0) { + computeAllVerts(); // XXXX: lazy evaluate + return; + } + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + computeAllVerts(); // XXXX: lazy evaluate + } + + /** + * Returns the equations of the bounding planes for this bounding polytope. + * The equations are copied into the specified array. The array must be + * large enough to hold all of the vectors. The individual array elements + * must be allocated by the caller. + * + * @param planes + * an array Vector4d to receive the bounding planes + */ + public void getPlanes(Vector4d[] planes) { + int i; + for (i = 0; i < planes.length; i++) { + planes[i].x = this.planes[i].x * mag[i]; + planes[i].y = this.planes[i].y * mag[i]; + planes[i].z = this.planes[i].z * mag[i]; + planes[i].w = this.planes[i].w * mag[i]; + } + } + + public int getNumPlanes() { + return planes.length; + } + + /** + * Sets the planes for this BoundingPolytope by keeping its current + * number and position of planes and computing new planes positions + * to enclose the given bounds object. + * @param boundsObject another bounds object + */ + public void set(Bounds boundsObject) { + int i,k; + double dis; + + // no polytope exists yet so initialize one using the boundsObject + if( boundsObject == null ) { + boundsIsEmpty = true; + boundsIsInfinite = false; + computeAllVerts(); // XXXX: lazy evaluate + }else if( boundsObject.boundId == BOUNDING_SPHERE ) { + BoundingSphere sphere = (BoundingSphere)boundsObject; + if( boundsIsEmpty) { + initEmptyPolytope(); // no ptope exist so must initialize to default + computeAllVerts(); + } + for(i=0;i 0.0) planes[i].w = -newD; + if( (newD = ux + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = ux + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + uy + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + } + boundsIsEmpty = boundsObject.boundsIsEmpty; + boundsIsInfinite = boundsObject.boundsIsInfinite; + computeAllVerts(); // XXXX: lazy evaluate + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope polytope = (BoundingPolytope)boundsObject; + if( planes.length != polytope.planes.length) { + planes = new Vector4d[polytope.planes.length]; + for(k=0;k 0.0 ) { + planes[i].w += -dis; + } + } + } else if( boundsObject instanceof BoundingBox){ + BoundingBox b = (BoundingBox)boundsObject; + if( !allocBoxVerts){ + boxVerts = new Point3d[8]; + for(int j=0;j<8;j++)boxVerts[j] = new Point3d(); + allocBoxVerts = true; + } + boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z ); + boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z ); + boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z ); + boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z ); + boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z ); + boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z ); + boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z ); + boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z ); + this.combine(boxVerts); + + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope polytope = (BoundingPolytope)boundsObject; + this.combine(polytope.verts); + } else { + } + + computeAllVerts(); + } + + /** + * Combines this bounding polytope with a point. + * @param point a 3d point in space + */ + public void combine(Point3d point) { + int i; + double dis; + + if(boundsIsInfinite) { + return; + } + + if( boundsIsEmpty ){ + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + nVerts = 1; + verts = new Point3d[nVerts]; + verts[0] = new Point3d( point.x, point.y, point.z); + + for(i=0;i 0.0 ) { + planes[i].w += -dis; + } + } + computeAllVerts(); + } + } + + /** + * Combines this bounding polytope with an array of points. + * @param points an array of 3d points in space + */ + public void combine(Point3d[] points) { + int i,j; + double dis; + + if( boundsIsInfinite) { + return; + } + + if( boundsIsEmpty ){ + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + nVerts = points.length; + verts = new Point3d[nVerts]; + verts[0] = new Point3d( points[0].x, points[0].y, points[0].z); + + for(i=0;i 0.0 ) { + planes[i].w += -dis; + } + } + } + + computeAllVerts(); + } + + /** + * Transforms this bounding polytope by the given transformation matrix. + * + * @param matrix + * a transformation matrix + */ + @Override + public void transform(Transform3D matrix) { + int i; + double invMag; + Transform3D invTrans = new Transform3D(matrix); + invTrans.invert(); + invTrans.transpose(); + for (i = 0; i < planes.length; i++) { + planes[i].x = planes[i].x * mag[i]; + planes[i].y = planes[i].y * mag[i]; + planes[i].z = planes[i].z * mag[i]; + planes[i].w = planes[i].w * mag[i]; + invTrans.transform(planes[i]); + } + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + for (i = 0; i < verts.length; i++) { + matrix.transform(verts[i]); + } + } + + /** + * Test for intersection with another bounds object. + * @param boundsObject another bounds object + * @return true or false indicating if an intersection occured + */ + public boolean intersect(Bounds boundsObject) { + + if( boundsObject == null ) { + return false; + } + + if( boundsIsEmpty || boundsObject.boundsIsEmpty ) { + return false; + } + + if( boundsIsInfinite || boundsObject.boundsIsInfinite ) { + return true; + } + + if( boundsObject.boundId == BOUNDING_SPHERE ) { + return intersect_ptope_sphere( this, (BoundingSphere)boundsObject); + } else if( boundsObject.boundId == BOUNDING_BOX){ + return intersect_ptope_abox( this, (BoundingBox)boundsObject); + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + return intersect_ptope_ptope( this, (BoundingPolytope)boundsObject); + } else { + return false; + } + } + + private void computeVertex(int a, int b, int c) { + double det, x, y, z; + det = planes[a].x * planes[b].y * planes[c].z + planes[a].y + * planes[b].z * planes[c].x + planes[a].z * planes[b].x + * planes[c].y - planes[a].z * planes[b].y * planes[c].x + - planes[a].y * planes[b].x * planes[c].z - planes[a].x + * planes[b].z * planes[c].y; + // System.err.println("\n det="+det); + if (det * det < EPSILON) { + // System.err.println("parallel planes="+a+" "+b+" "+c); + return; // two planes are parallel + } + det = 1.0 / det; + x = (planes[b].y * planes[c].z - planes[b].z * planes[c].y) * pDotN[a]; + y = (planes[b].z * planes[c].x - planes[b].x * planes[c].z) * pDotN[a]; + z = (planes[b].x * planes[c].y - planes[b].y * planes[c].x) * pDotN[a]; + x += (planes[c].y * planes[a].z - planes[c].z * planes[a].y) * pDotN[b]; + y += (planes[c].z * planes[a].x - planes[c].x * planes[a].z) * pDotN[b]; + z += (planes[c].x * planes[a].y - planes[c].y * planes[a].x) * pDotN[b]; + x += (planes[a].y * planes[b].z - planes[a].z * planes[b].y) * pDotN[c]; + y += (planes[a].z * planes[b].x - planes[a].x * planes[b].z) * pDotN[c]; + z += (planes[a].x * planes[b].y - planes[a].y * planes[b].x) * pDotN[c]; + x = x * det; + y = y * det; + z = z * det; + if (pointInPolytope(x, y, z)) { + if (nVerts >= verts.length) { + Point3d newVerts[] = new Point3d[nVerts << 1]; + for (int i = 0; i < nVerts; i++) { + newVerts[i] = verts[i]; + } + verts = newVerts; + } + verts[nVerts++] = new Point3d(x, y, z); + } + } + + private void computeAllVerts() { + int i, a, b, c; + double x, y, z; + nVerts = 0; + verts = new Point3d[planes.length * planes.length]; + for (i = 0; i < planes.length; i++) { + pDotN[i] = -planes[i].x * planes[i].w * planes[i].x - planes[i].y + * planes[i].w * planes[i].y - planes[i].z * planes[i].w + * planes[i].z; + } + for (a = 0; a < planes.length - 2; a++) { + for (b = a + 1; b < planes.length - 1; b++) { + for (c = b + 1; c < planes.length; c++) { + computeVertex(a, b, c); + } + } + } + // XXXX: correctly compute centroid + x = y = z = 0.0; + Point3d newVerts[] = new Point3d[nVerts]; + for (i = 0; i < nVerts; i++) { + x += verts[i].x; + y += verts[i].y; + z += verts[i].z; + // copy the verts into an array of the correct size + newVerts[i] = verts[i]; + } + this.verts = newVerts; // copy the verts into an array of the correct + // size + centroid.x = x / nVerts; + centroid.y = y / nVerts; + centroid.z = z / nVerts; + } + + private boolean pointInPolytope(double x, double y, double z) { + for (int i = 0; i < planes.length; i++) { + if ((x * planes[i].x + y * planes[i].y + z * planes[i].z + planes[i].w) > EPSILON) { + return false; + } + } + return true; + } + + private void checkBoundsIsEmpty() { + boundsIsEmpty = (planes.length < 4); + } + + private void initEmptyPolytope() { + planes = new Vector4d[6]; + pDotN = new double[6]; + mag = new double[6]; + verts = new Point3d[planes.length*planes.length]; + nVerts = 0; + + planes[0] = new Vector4d( 1.0, 0.0, 0.0, -1.0 ); + planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0 ); + planes[2] = new Vector4d( 0.0, 1.0, 0.0, -1.0 ); + planes[3] = new Vector4d( 0.0,-1.0, 0.0, -1.0 ); + planes[4] = new Vector4d( 0.0, 0.0, 1.0, -1.0 ); + planes[5] = new Vector4d( 0.0, 0.0,-1.0, -1.0 ); + mag[0] = 1.0; + mag[1] = 1.0; + mag[2] = 1.0; + mag[3] = 1.0; + mag[4] = 1.0; + mag[5] = 1.0; + checkBoundsIsEmpty(); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingSphere.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingSphere.java new file mode 100644 index 0000000..8b9cf8a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BoundingSphere.java @@ -0,0 +1,93 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class BoundingSphere extends Bounds { + public Point3d center = new Point3d(); + public double radius; + + public BoundingSphere(Point3d center, double r) { + boundId = BOUNDING_SPHERE; + this.center = center; + this.radius = r; + } + + public BoundingSphere() { + boundId = BOUNDING_SPHERE; + center.x = 0; + center.y = 0; + center.z = 0; + radius = 1; + } + + public void combine(BoundingSphere bs) { + double r1 = this.radius; + double r2 = bs.radius; + + Point3d c1 = new Point3d(center); + Point3d c2 = new Point3d(bs.center); + + double d = center.distance(bs.center); + + if (d + r2 <= r1) { + // this が bs を包含する場合、何もしない + } else if (d + r1 < r2) { + // bs が this を包含する場合、bsをコピー + this.radius = bs.radius; + this.center.set(bs.center); + } else { + // this と  bs のいずれも他方を包含しない場合 + this.radius = (r1 + r2 + d) / 2.0; + + double a1 = this.radius - r1; + double a2 = this.radius - r2; + + c1.scale(a2); + c2.scale(a1); + c1.add(c2); + c1.scale(1 / d); + center.set(c1); + } + } + + public void transform(Transform3D trans) { + Vector4d v = new Vector4d(center.x, center.y, center.z, 1.0); + trans.transform(v); + center.set(v.x, v.y, v.z); + + double max = 0.0; + for(int i = 0; i < trans.scales.length; i++) { + if (max < trans.scales[i]) { + max = trans.scales[i]; + } + } + radius *= max; + } + + @Override + public Object clone() { + return new BoundingSphere(new Point3d(this.center), this.radius); + } + + @Override + public void combine(Bounds boundsObject) { + if( boundsObject instanceof BoundingSphere) { + combine((BoundingSphere) boundsObject); + } + } + + @Override + public boolean intersect(Bounds boundsObject) { + if (boundsObject instanceof BoundingSphere) { + BoundingSphere bs = (BoundingSphere) boundsObject; + double r = this.radius + bs.radius; + double x = center.x - bs.center.x; + double y = center.y - bs.center.y; + double z = center.z - bs.center.z; + double d = x * x + y * y + z * z; + if (d <= r * r) { + return true; + } + } + return false; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Bounds.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Bounds.java new file mode 100644 index 0000000..bca2a74 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Bounds.java @@ -0,0 +1,459 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Bounds extends Object implements Cloneable { + static final double EPSILON = .000001; + static final boolean debug = false; + static final int BOUNDING_BOX = 0x1; + static final int BOUNDING_SPHERE = 0x2; + static final int BOUNDING_POLYTOPE = 0x4; + boolean boundsIsEmpty = false; + boolean boundsIsInfinite = false; + int boundId = 0; + + public Bounds(){ + + } + + // public Bounds clone() { + // return new Bounds(); + // } + + @Override + public abstract Object clone(); + + public abstract void combine(Bounds boundsObject); + + public abstract void transform(Transform3D trans); + + public abstract boolean intersect(Bounds boundsObject); + + + + // //浸食確認メソッド。radius(半径)を利用した円の接触であると仮定してみるといいかも + // public boolean intersect(BoundingSphere bs) { + // // TODO Auto-generated method stub + // return false; + // } + + private void test_point(Vector4d[] planes, Point3d new_point) { + for (int i = 0; i < planes.length; i++){ + double dist = (new_point.x*planes[i].x + new_point.y*planes[i].y + + new_point.z*planes[i].z + planes[i].w ) ; + if (dist > EPSILON ){ + System.err.println("new point is outside of" + + " plane["+i+"] dist = " + dist); + } + } + } + + /** + * computes the closest point from the given point to a set of planes + * (polytope) + * @param g the point + * @param planes array of bounding planes + * @param new_point point on planes closest g + */ + boolean closest_point(Point3d g, Vector4d[] planes, Point3d new_point) { + + double t, s, dist, w; + boolean converged, inside, firstPoint, firstInside; + int i, count; + double ab, ac, bc, ad, bd, cd, aa, bb, cc; + double b1, b2, b3, d1, d2, d3, y1, y2, y3; + double h11, h12, h13, h22, h23, h33; + double l12, l13, l23; + Point3d n = new Point3d(); + Point3d p = new Point3d(); + Vector3d delta = null; + + // These are temporary until the solve code is working + + /* + * The algorithm: We want to find the point "n", closest to "g", while + * still within the the polytope defined by "planes". We find the + * solution by minimizing the value for a "penalty function"; + * + * f = distance(n,g)^2 + sum for each i: w(distance(n, planes[i])) + * + * Where "w" is a weighting which indicates how much more important it + * is to be close to the planes than it is to be close to "g". + * + * We minimize this function by taking it's derivitive, and then solving + * for the value of n when the derivitive equals 0. + * + * For the 1D case with a single plane (a,b,c,d), x = n.x and g = g.x, + * this looks like: + * + * f(x) = (x - g) ^ 2 + w(ax + d)^2 f'(x) = 2x -2g + 2waax + 2wad + * + * (note aa = a^2) setting f'(x) = 0 gives: + * + * (1 + waa)x = g - wad + * + * Note that the solution is just outside the plane [a, d]. With the + * correct choice of w, this should be inside of the EPSILON tolerance + * outside the planes. + * + * Extending to 3D gives the matrix solution: + * + * | (1 + waa) wab wac | H = | wab (1 + wbb) wbc | | wac wbc (1 + wcc) | + * + * b = [g.x - wad, g.y - wbd, g.z - wcd] + * + * H * n = b + * + * n = b * H.inverse() + * + * The implementation speeds this process up by recognizing that H is + * symmetric, so that it can be decomposed into three matrices: + * + * H = L * D * L.transpose() + * + * 1.0 0.0 0.0 d1 0.0 0.0 L = l12 1.0 0.0 D = 0.0 d2 0.0 l13 l23 1.0 0.0 + * 0.0 d3 + * + * n can then be derived by back-substitution, where the original + * problem is decomposed as: + * + * H * n = b L * D * L.transpose() * n = b L * D * y = b; L.transpose() + * * n = y + * + * We can then multiply out the terms of L * D and solve for y, and then + * use y to solve for n. + */ + + w = 100.0 / EPSILON; // must be large enough to ensure that solution + // is within EPSILON of planes + + count = 0; + p.set(g); + + if (debug) { + System.err.println("closest_point():\nincoming g=" + " " + g.x + + " " + g.y + " " + g.z); + } + + converged = false; + firstPoint = true; + firstInside = false; + + Vector4d pln; + + while (!converged) { + if (debug) { + System.err.println("start: p=" + " " + p.x + " " + p.y + " " + + p.z); + } + + // test the current point against the planes, for each + // plane that is violated, add it's contribution to the + // penalty function + inside = true; + aa = 0.0; + bb = 0.0; + cc = 0.0; + ab = 0.0; + ac = 0.0; + bc = 0.0; + ad = 0.0; + bd = 0.0; + cd = 0.0; + for (i = 0; i < planes.length; i++) { + pln = planes[i]; + dist = (p.x * pln.x + p.y * pln.y + p.z * pln.z + pln.w); + // if point is outside or within EPSILON of the boundary, add + // the plane to the penalty matrix. We do this even if the + // point is already inside the polytope to prevent numerical + // instablity in cases where the point is just outside the + // boundary of several planes of the polytope + if (dist > -EPSILON) { + aa = aa + pln.x * pln.x; + bb = bb + pln.y * pln.y; + cc = cc + pln.z * pln.z; + ab = ab + pln.x * pln.y; + ac = ac + pln.x * pln.z; + bc = bc + pln.y * pln.z; + ad = ad + pln.x * pln.w; + bd = bd + pln.y * pln.w; + cd = cd + pln.z * pln.w; + } + // If the point is inside if dist is <= EPSILON + if (dist > EPSILON) { + inside = false; + if (debug) { + System.err.println("point outside plane[" + i + "]=(" + + pln.x + "," + pln.y + ",\n\t" + pln.z + "," + + pln.w + ")\ndist = " + dist); + } + } + } + // see if we are done + if (inside) { + if (debug) { + System.err.println("p is inside"); + } + if (firstPoint) { + firstInside = true; + } + new_point.set(p); + converged = true; + } else { // solve for a closer point + firstPoint = false; + + // this is the upper right corner of H, which is all we + // need to do the decomposition since the matrix is symetric + h11 = 1.0 + aa * w; + h12 = ab * w; + h13 = ac * w; + h22 = 1.0 + bb * w; + h23 = bc * w; + h33 = 1.0 + cc * w; + + if (debug) { + System.err.println(" hessin= "); + System.err.println(h11 + " " + h12 + " " + h13); + System.err.println(" " + h22 + " " + h23); + System.err.println(" " + h33); + } + + // these are the constant terms + b1 = g.x - w * ad; + b2 = g.y - w * bd; + b3 = g.z - w * cd; + + if (debug) { + System.err.println(" b1,b2,b3 = " + b1 + " " + b2 + " " + + b3); + } + + // solve, d1, d2, d3 actually 1/dx, which is more useful + d1 = 1 / h11; + l12 = d1 * h12; + l13 = d1 * h13; + s = h22 - l12 * h12; + d2 = 1 / s; + t = h23 - h12 * l13; + l23 = d2 * t; + d3 = 1 / (h33 - h13 * l13 - t * l23); + + if (debug) { + System.err.println(" l12,l13,l23 " + l12 + " " + l13 + " " + + l23); + System.err.println(" d1,d2,d3 " + d1 + " " + d2 + " " + d3); + } + + // we have L and D, now solve for y + y1 = d1 * b1; + y2 = d2 * (b2 - h12 * y1); + y3 = d3 * (b3 - h13 * y1 - t * y2); + + if (debug) { + System.err.println(" y1,y2,y3 = " + y1 + " " + y2 + " " + + y3); + } + + // we have y, solve for n + n.z = y3; + n.y = (y2 - l23 * n.z); + n.x = (y1 - l13 * n.z - l12 * n.y); + + if (debug) { + System.err.println("new point = " + n.x + " " + n.y + " " + + n.z); + test_point(planes, n); + + if (delta == null) + delta = new Vector3d(); + delta.sub(n, p); + delta.normalize(); + System.err.println("p->n direction: " + delta); + Matrix3d hMatrix = new Matrix3d(); + // check using the the javax.vecmath routine + hMatrix.m00 = h11; + hMatrix.m01 = h12; + hMatrix.m02 = h13; + hMatrix.m10 = h12; // h21 = h12 + hMatrix.m11 = h22; + hMatrix.m12 = h23; + hMatrix.m20 = h13; // h31 = h13 + hMatrix.m21 = h23; // h32 = h22 + hMatrix.m22 = h33; + hMatrix.invert(); + Point3d check = new Point3d(b1, b2, b3); + hMatrix.transform(check); + + System.err.println("check point = " + check.x + " " + + check.y + " " + check.z); + } + + // see if we have converged yet + dist = (p.x - n.x) * (p.x - n.x) + (p.y - n.y) * (p.y - n.y) + + (p.z - n.z) * (p.z - n.z); + + if (debug) { + System.err.println("p->n distance =" + dist); + } + + if (dist < EPSILON) { // close enough + converged = true; + new_point.set(n); + } else { + p.set(n); + count++; + if (count > 4) { // watch for cycling between two minimums + new_point.set(n); + converged = true; + } + } + } + } + if (debug) { + System.err.println("returning pnt (" + new_point.x + " " + + new_point.y + " " + new_point.z + ")"); + + if (firstInside) + System.err.println("input point inside polytope "); + } + return firstInside; + } + + boolean intersect_ptope_sphere( BoundingPolytope polyTope, + BoundingSphere sphere) { + Point3d p = new Point3d(); + boolean inside; + + + if (debug) { + System.err.println("ptope_sphere intersect sphere ="+sphere); + } + inside = closest_point( sphere.center, polyTope.planes, p ); + if (debug) { + System.err.println("ptope sphere intersect point ="+p); + } + if (!inside){ + // if distance between polytope and sphere center is greater than + // radius then no intersection + if (p.distanceSquared( sphere.center) > + sphere.radius*sphere.radius){ + if (debug) { + System.err.println("ptope_sphere returns false"); + } + return false; + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } + + boolean intersect_ptope_abox( BoundingPolytope polyTope, BoundingBox box) { + Vector4d planes[] = new Vector4d[6]; + + if (debug) { + System.err.println("ptope_abox, box = " + box); + } + planes[0] = new Vector4d( -1.0, 0.0, 0.0, box.lower.x); + planes[1] = new Vector4d( 1.0, 0.0, 0.0,-box.upper.x); + planes[2] = new Vector4d( 0.0,-1.0, 0.0, box.lower.y); + planes[3] = new Vector4d( 0.0, 1.0, 0.0,-box.upper.y); + planes[4] = new Vector4d( 0.0, 0.0,-1.0, box.lower.z); + planes[5] = new Vector4d( 0.0, 0.0, 1.0,-box.upper.z); + + + BoundingPolytope pbox = new BoundingPolytope( planes); + + boolean result = intersect_ptope_ptope( polyTope, pbox ); + if (debug) { + System.err.println("ptope_abox returns " + result); + } + return(result); + } + + + boolean intersect_ptope_ptope( BoundingPolytope poly1, + BoundingPolytope poly2) { + boolean intersect; + Point3d p = new Point3d(); + Point3d g = new Point3d(); + Point3d gnew = new Point3d(); + Point3d pnew = new Point3d(); + + intersect = false; + + p.x = 0.0; + p.y = 0.0; + p.z = 0.0; + + // start from an arbitrary point on poly1 + closest_point( p, poly1.planes, g); + + // get the closest points on each polytope + if (debug) { + System.err.println("ptope_ptope: first g = "+g); + } + intersect = closest_point( g, poly2.planes, p); + + if (intersect) { + return true; + } + + if (debug) { + System.err.println("first p = "+p+"\n"); + } + intersect = closest_point( p, poly1.planes, gnew); + if (debug) { + System.err.println("gnew = "+gnew+" intersect="+intersect); + } + + // loop until the closest points on the two polytopes are not changing + + double prevDist = p.distanceSquared(g); + double dist; + + while( !intersect ) { + + dist = p.distanceSquared(gnew); + + if (dist < prevDist) { + g.set(gnew); + intersect = closest_point( g, poly2.planes, pnew ); + if (debug) { + System.err.println("pnew = "+pnew+" intersect="+intersect); + } + } else { + g.set(gnew); + break; + } + prevDist = dist; + dist = pnew.distanceSquared(g); + + if (dist < prevDist) { + p.set(pnew); + if( !intersect ) { + intersect = closest_point( p, poly1.planes, gnew ); + if (debug) { + System.err.println("gnew = "+gnew+" intersect="+ + intersect); + } + } + } else { + p.set(pnew); + break; + } + prevDist = dist; + } + + if (debug) { + System.err.println("gnew="+" "+gnew.x+" "+gnew.y+" "+gnew.z); + System.err.println("pnew="+" "+pnew.x+" "+pnew.y+" "+pnew.z); + } + return intersect; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Box.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Box.java new file mode 100644 index 0000000..2688c7f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Box.java @@ -0,0 +1,238 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Box extends Primitive { + public static final int FRONT = 0; + public static final int BACK = 1; + public static final int RIGHT = 2; + public static final int LEFT = 3; + public static final int TOP = 4; + public static final int BOTTOM = 5; + + float xDim = 1.0f; + float yDim = 1.0f; + float zDim = 1.0f; + + private Shape3D frontShape = null; + private Shape3D backShape = null; + private Shape3D rightShape = null; + private Shape3D leftShape = null; + private Shape3D topShape = null; + private Shape3D bottomShape = null; + + public Box() { + this(1.0f, 1.0f, 1.0f, null); + } + + public Box(float x, float y, float z, Appearance ap) { + float coordinates[][] = { + {-x, -y, z}, + { x, -y, z}, + { x, y, z}, + {-x, y, z}, + {-x, -y, -z}, + { x, -y, -z}, + { x, y, -z}, + {-x, y, -z} + }; + + float uv[] = { + 0, 0, 1, 0, 1, 1 , 0, 1, + }; + + // 各面のジオメトリを作成 + TriangleFanArray frontGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray backGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray rightGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray leftGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray topGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray bottomGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + + // 頂点座標の設定 + frontGeom.setCoordinate(0, coordinates[0]); + frontGeom.setCoordinate(1, coordinates[1]); + frontGeom.setCoordinate(2, coordinates[2]); + frontGeom.setCoordinate(3, coordinates[3]); + + backGeom.setCoordinate(0, coordinates[5]); + backGeom.setCoordinate(1, coordinates[4]); + backGeom.setCoordinate(2, coordinates[7]); + backGeom.setCoordinate(3, coordinates[6]); + + rightGeom.setCoordinate(0, coordinates[1]); + rightGeom.setCoordinate(1, coordinates[5]); + rightGeom.setCoordinate(2, coordinates[6]); + rightGeom.setCoordinate(3, coordinates[2]); + + leftGeom.setCoordinate(0, coordinates[4]); + leftGeom.setCoordinate(1, coordinates[0]); + leftGeom.setCoordinate(2, coordinates[3]); + leftGeom.setCoordinate(3, coordinates[7]); + + topGeom.setCoordinate(0, coordinates[3]); + topGeom.setCoordinate(1, coordinates[2]); + topGeom.setCoordinate(2, coordinates[6]); + topGeom.setCoordinate(3, coordinates[7]); + + bottomGeom.setCoordinate(0, coordinates[4]); + bottomGeom.setCoordinate(1, coordinates[5]); + bottomGeom.setCoordinate(2, coordinates[1]); + bottomGeom.setCoordinate(3, coordinates[0]); + + // テクスチャ座標の設定 + frontGeom.setTextureCoordinates(0, uv); + backGeom.setTextureCoordinates(0, uv); + rightGeom.setTextureCoordinates(0, uv); + leftGeom.setTextureCoordinates(0, uv); + topGeom.setTextureCoordinates(0, uv); + bottomGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + float[] frontNorm = new float[]{0.0f, 0.0f, 1.0f}; + frontGeom.setNormal(0, frontNorm); + frontGeom.setNormal(1, frontNorm); + frontGeom.setNormal(2, frontNorm); + frontGeom.setNormal(3, frontNorm); + + float[] backNorm = new float[]{0.0f, 0.0f, -1.0f}; + backGeom.setNormal(0, backNorm); + backGeom.setNormal(1, backNorm); + backGeom.setNormal(2, backNorm); + backGeom.setNormal(3, backNorm); + + float[] rightNorm = new float[]{1.0f, 0.0f, 0.0f}; + rightGeom.setNormal(0, rightNorm); + rightGeom.setNormal(1, rightNorm); + rightGeom.setNormal(2, rightNorm); + rightGeom.setNormal(3, rightNorm); + + float[] leftNorm = new float[]{-1.0f, 0.0f, 0.0f}; + leftGeom.setNormal(0, leftNorm); + leftGeom.setNormal(1, leftNorm); + leftGeom.setNormal(2, leftNorm); + leftGeom.setNormal(3, leftNorm); + + float[] topNorm = new float[]{0.0f, 1.0f, 0.0f}; + topGeom.setNormal(0, topNorm); + topGeom.setNormal(1, topNorm); + topGeom.setNormal(2, topNorm); + topGeom.setNormal(3, topNorm); + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + bottomGeom.setNormal(0, bottomNorm); + bottomGeom.setNormal(1, bottomNorm); + bottomGeom.setNormal(2, bottomNorm); + bottomGeom.setNormal(3, bottomNorm); + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); + Appearance ap1; + Appearance ap2; + Appearance ap3; + Appearance ap4; + Appearance ap5; + Appearance ap6; + Texture tex = ap.getTexture(); + if (tex != null && tex instanceof TextureCubeMap) { + // GL10 では GL_TEXTURE_CUBE_MAP が使えないので、TextureCubeMap の場合は Texture2D に分解する + ap1 = (Appearance)ap.cloneNodeComponent(); + ap2 = (Appearance)ap.cloneNodeComponent(); + ap3 = (Appearance)ap.cloneNodeComponent(); + ap4 = (Appearance)ap.cloneNodeComponent(); + ap5 = (Appearance)ap.cloneNodeComponent(); + ap6 = (Appearance)ap.cloneNodeComponent(); + ImageComponent ic1 = tex.getImage(TextureCubeMap.POSITIVE_Z); + Texture2D tex1 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic1.width, ic1.height); + tex1.setImage(0, ic1); + ap1.setTexture(tex1); + ImageComponent ic2 = tex.getImage(TextureCubeMap.NEGATIVE_Z); + Texture2D tex2 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic2.width, ic2.height); + tex2.setImage(0, ic2); + ap2.setTexture(tex2); + ImageComponent ic3 = tex.getImage(TextureCubeMap.POSITIVE_X); + Texture2D tex3 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic3.width, ic3.height); + tex3.setImage(0, ic3); + ap3.setTexture(tex3); + ImageComponent ic4 = tex.getImage(TextureCubeMap.NEGATIVE_X); + Texture2D tex4 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic4.width, ic4.height); + tex4.setImage(0, ic4); + ap4.setTexture(tex4); + ImageComponent ic5 = tex.getImage(TextureCubeMap.POSITIVE_Y); + Texture2D tex5 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic5.width, ic5.height); + tex5.setImage(0, ic5); + ap5.setTexture(tex5); + ImageComponent ic6 = tex.getImage(TextureCubeMap.NEGATIVE_Y); + Texture2D tex6 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic6.width, ic6.height); + tex6.setImage(0, ic6); + ap6.setTexture(tex6); + } else { + ap1 = ap2 = ap3 = ap4 = ap5 = ap6 = ap; + } + + // 各面の作成 + frontShape = new Shape3D(frontGeom, ap1); + backShape = new Shape3D(backGeom, ap2); + rightShape = new Shape3D(rightGeom, ap3); + leftShape = new Shape3D(leftGeom, ap4); + topShape = new Shape3D(topGeom, ap5); + bottomShape = new Shape3D(bottomGeom, ap6); + + xDim = x; + yDim = y; + zDim = z; + } + + public double getXdimension() { + return xDim; + } + + public double getYdimension() { + return yDim; + } + + public double getZdimension() { + return zDim; + } + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case FRONT: + return frontShape; + case BACK: + return backShape; + case RIGHT: + return rightShape; + case LEFT: + return leftShape; + case TOP: + return topShape; + case BOTTOM: + return bottomShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Box b = new Box(xDim, yDim, zDim, ap); + return b; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BranchGroup.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BranchGroup.java new file mode 100644 index 0000000..6b305bf --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/BranchGroup.java @@ -0,0 +1,6 @@ +package com.google.ar.core.examples.java.common.java3d; + + +public class BranchGroup extends Group { + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color3f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color3f.java new file mode 100644 index 0000000..e7b86e2 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color3f.java @@ -0,0 +1,26 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Color3f { + public float r; + public float g; + public float b; + + // コンストラクタ + public Color3f() { + r = 0.0f; + g = 0.0f; + b = 0.0f; + } + + // コンストラクタ + public Color3f(float pr, float pg, float pb) { + r = pr; + g = pg; + b = pb; + } + + public Color3f clone() { + return new Color3f(r, g, b); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color4f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color4f.java new file mode 100644 index 0000000..f3f7652 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Color4f.java @@ -0,0 +1,29 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Color4f { + public float r; + public float g; + public float b; + public float a; + + // コンストラクタ + public Color4f() { + r = 0.0f; + g = 0.0f; + b = 0.0f; + a = 0.0f; + } + + // コンストラクタ + public Color4f(float pr, float pg, float pb, float pa) { + r = pr; + g = pg; + b = pb; + a = pa; + } + + public Color4f clone() { + return new Color4f(r, g, b, a); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cone.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cone.java new file mode 100644 index 0000000..2fecafe --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cone.java @@ -0,0 +1,129 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Cone extends Primitive { + public static final int BODY = 0; + public static final int CAP = 1; + + static final int MID_REZ_DIV_X = 16; + static final int MID_REZ_DIV_Y = 1; + + float radius; + float height; + int xdivisions; + int ydivisions; + + + private Shape3D bodyShape = null; + private Shape3D capShape = null; + + public Cone() { + this(0.5f, 1.0f, null); + } + + public Cone(float r, float h, Appearance ap) { + xdivisions = MID_REZ_DIV_X; + ydivisions = MID_REZ_DIV_Y; + + float coordinates[][] = new float[xdivisions][3]; + + for (int i = 0; i < xdivisions; i++) { // BOTTOM(底面)の点の(x,y,z)座標 + coordinates[i][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i][1] = -h / 2; + coordinates[i][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + + float coordinates_center[][] = new float[2][3];//頂点と,底面の中心,の座標 + coordinates_center[0][0] = 0; + coordinates_center[0][1] = h / 2; + coordinates_center[0][2] = 0; + coordinates_center[1][0] = 0; + coordinates_center[1][1] = -h / 2; + coordinates_center[1][2] = 0; + + float uv[] = { 0, 0, 1, 0, 1, 1, 0, 1, }; + + // 各面のジオメトリを作成 + TriangleFanArray bodyGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + TriangleFanArray capGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + + // 頂点座標の設定 + //側面 + bodyGeom.setCoordinate(0,coordinates_center[0]); + for (int i = 0; i < xdivisions; i++){ + bodyGeom.setCoordinate(i + 1, coordinates[i]); //上から見て反時計回りの順 + } + bodyGeom.setCoordinate(xdivisions + 1, coordinates[0]); + //底面 + capGeom.setCoordinate(0,coordinates_center[1]); + capGeom.setCoordinate(1, coordinates[0]); + for (int i = 0; i < xdivisions; i++){ + capGeom.setCoordinate(i + 2, coordinates[xdivisions - i - 1]); //上から見て時計回りの順 + } + + // テクスチャ座標の設定 + bodyGeom.setTextureCoordinates(0, uv); // + capGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + double theta; + theta = Math.atan(h / r); + for (int i = 0; i < xdivisions; i++) { + float[] bodyNorm = new float[]{ + (float)(r * Math.sin(theta) * Math.cos(90 - theta) * Math.cos(2 * Math.PI * ((double)i / xdivisions))), + (float)(r * Math.sin(theta) * Math.sin(90 - theta)), + (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)) + }; + bodyGeom.setNormal(i, bodyNorm); + } + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + capGeom.setNormal(i, bottomNorm); + } + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); + + // 各面の作成 //全て同じテクスチャー(ap)を貼り付ける + bodyShape = new Shape3D(bodyGeom, ap); + capShape = new Shape3D(capGeom, ap); + + radius = r; + height = h; + } + + public double getRadius() { + return radius; + } + public double getHeight() { + return height; + } + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case BODY: + return bodyShape; + case CAP: + return capShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Cone c = new Cone(radius, height, ap); + return c; + } +} + diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cylinder.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cylinder.java new file mode 100644 index 0000000..92f8cb2 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Cylinder.java @@ -0,0 +1,203 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Cylinder extends Primitive { + public static final int BODY = 0; + public static final int TOP = 1; + public static final int BOTTOM = 2; + + static final int MID_REZ_DIV_X = 16; + static final int MID_REZ_DIV_Y = 1; + + float radius; + float height; + int xdivisions; + int ydivisions; + + private Shape3D bodyShape = null; + private Shape3D topShape = null; + private Shape3D bottomShape = null; + + public Cylinder() { + this(0.5f, 1.0f, null); + } + + public Cylinder(float r, float h) { + this(r, h, null); + } + + public Cylinder(float r, float h, Appearance ap) { + xdivisions = MID_REZ_DIV_X; + ydivisions = MID_REZ_DIV_Y; + + float coordinates[][] = new float[xdivisions * 2][3]; + + float coordinates_center[][] = new float[2][3];//上面と底面の中心の座標 + coordinates_center[0][0] = 0; + coordinates_center[0][1] = h / 2; + coordinates_center[0][2] = 0; + coordinates_center[1][0] = 0; + coordinates_center[1][1] = -h / 2; + coordinates_center[1][2] = 0; + + for (int i = 0; i < xdivisions; i++) { // TOP(上面)側の点の(x,y,z)座標 + coordinates[i][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i][1] = h / 2; + coordinates[i][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + for (int i = 0; i < xdivisions; i++) { // BOTTOM(底面)の点の(x,y,z)座標 + coordinates[i + xdivisions][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i + xdivisions][1] = -h / 2; + coordinates[i + xdivisions][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + + float uv[] = { 0, 0, 1, 0, 1, 1, 0, 1, }; + + // 各面のジオメトリを作成 + IndexedTriangleStripArray bodyGeom = new IndexedTriangleStripArray(xdivisions * 2, + IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.NORMALS | IndexedTriangleStripArray.TEXTURE_COORDINATE_2, + xdivisions * 2 + 2, + new int[] { xdivisions * 2 + 2 }); + TriangleFanArray topGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + TriangleFanArray bottomGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + + // 頂点座標の設定 + //側面 + for (int i = 0; i < xdivisions; i++) { //上面から底面へ垂直に降りて、底面から上面へ斜めに上る順に座標を設定 + bodyGeom.setCoordinate(2 * i , coordinates[i]); + bodyGeom.setCoordinate(2 * i + 1, coordinates[i + xdivisions]); + } + + int coordinateIndices[] = new int[xdivisions * 2 + 2]; + for (int i = 0; i < xdivisions * 2; i++) { + coordinateIndices[i] = i; + } + coordinateIndices[xdivisions * 2] = 0; + coordinateIndices[xdivisions * 2 + 1] = 1; + + bodyGeom.setCoordinateIndices(0, coordinateIndices); + + //上面 + topGeom.setCoordinate(0,coordinates_center[0]); + for(int i = 0; i < xdivisions; i++){ + topGeom.setCoordinate(i + 1, coordinates[i]); //上から見て反時計回りの順 + } + topGeom.setCoordinate(xdivisions + 1, coordinates[0]); + + //底面 + bottomGeom.setCoordinate(0,coordinates_center[1]); + bottomGeom.setCoordinate(1, coordinates[xdivisions]); + for(int i = 0; i < xdivisions; i++){ + bottomGeom.setCoordinate(i + 2, coordinates[2 * xdivisions - i - 1]); //上から見て時計回りの順 + } + + // テクスチャ座標の設定 + bodyGeom.setTextureCoordinates(0, uv); // + topGeom.setTextureCoordinates(0, uv); + bottomGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + + for (int i = 0; i < xdivisions * 2; i++) { + float[] bodyNorm = new float[]{(float) Math.cos(Math.PI * ((double)i / xdivisions)),0.0f,(float) Math.sin(Math.PI * ((double)i / xdivisions))}; + bodyGeom.setNormal(i, bodyNorm); + } + + float[] topNorm = new float[]{0.0f, 1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + topGeom.setNormal(i, topNorm); + } + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + bottomGeom.setNormal(i, bottomNorm); + } + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); +// Appearance ap1 = new Appearance(); +// Appearance ap2 = new Appearance(); +// Appearance ap3 = new Appearance(); + // Appearance ap4 = new Appearance(); + // Appearance ap5 = new Appearance(); + // Appearance ap6 = new Appearance(); +// Texture tex = ap.getTexture(); +// if (tex != null && tex instanceof TextureCubeMap) { + // GL10 では GL_TEXTURE_CUBE_MAP が使えないので、TextureCubeMap の場合は Texture2D + // に分解する +// ImageComponent ic1 = tex.getImage(TextureCubeMap.POSITIVE_Z); +// Texture2D tex1 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic1.width, ic1.height); +// tex1.setImage(0, ic1); +// ap1.setTexture(tex1); +// ImageComponent ic2 = tex.getImage(TextureCubeMap.NEGATIVE_Z); +// Texture2D tex2 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic2.width, ic2.height); +// tex2.setImage(0, ic2); +// ap2.setTexture(tex2); +// ImageComponent ic3 = tex.getImage(TextureCubeMap.POSITIVE_X); +// Texture2D tex3 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic3.width, ic3.height); +// tex3.setImage(0, ic3); + // ap3.setTexture(tex3); + // ImageComponent ic4 = tex.getImage(TextureCubeMap.NEGATIVE_X); + // Texture2D tex4 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic4.width, ic4.height); + // tex4.setImage(0, ic4); + // ap4.setTexture(tex4); + // ImageComponent ic5 = tex.getImage(TextureCubeMap.POSITIVE_Y); + // Texture2D tex5 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic5.width, ic5.height); + // tex5.setImage(0, ic5); + // ap5.setTexture(tex5); + // ImageComponent ic6 = tex.getImage(TextureCubeMap.NEGATIVE_Y); + // Texture2D tex6 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic6.width, ic6.height); + // tex6.setImage(0, ic6); + // ap6.setTexture(tex6); +// } + // 各面の作成 //全て同じテクスチャー(ap)を貼り付ける + bodyShape = new Shape3D(bodyGeom, ap); + topShape = new Shape3D(topGeom, ap); + bottomShape = new Shape3D(bottomGeom, ap); + // + radius = r; + height = h; + } + + public double getRadius() { + return radius; + } + public double getHeight() { + return height; + } + + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case BODY: + return bodyShape; + case TOP: + return topShape; + case BOTTOM: + return bottomShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Cylinder c = new Cylinder(radius, height, ap); + return c; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/DirectionalLight.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/DirectionalLight.java new file mode 100644 index 0000000..9c38e52 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/DirectionalLight.java @@ -0,0 +1,26 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class DirectionalLight extends Light { + private Vector3f direction; + + @Override + public Node cloneTree() { + return new DirectionalLight(color, direction); + } + + public DirectionalLight(Color3f c, Vector3f d){ + super(c); + d.normalize(); + this.direction = d; + } + + public void setDirection(Vector3f d){ + d.normalize(); + this.direction = d; + } + + public Vector3f getDirection(){ + return direction; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Geometry.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Geometry.java new file mode 100644 index 0000000..a7e4f13 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Geometry.java @@ -0,0 +1,10 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Geometry extends NodeComponent { + public static final int ALLOW_INTERSECT = 18; + + // コンストラクタ + public Geometry() { + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryArray.java new file mode 100644 index 0000000..f4bf1b8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryArray.java @@ -0,0 +1,234 @@ +package com.google.ar.core.examples.java.common.java3d; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +public abstract class GeometryArray extends Geometry { +// public static final int ALLOW_COLOR_READ = 2; +// public static final int ALLOW_COLOR_WRITE = 3; +// public static final int ALLOW_COORDINATE_READ = 0; +// public static final int ALLOW_COORDINATE_WRITE = 1; +// public static final int ALLOW_COUNT_READ = 8; +// public static final int ALLOW_COUNT_WRITE = 20; +// public static final int ALLOW_FORMAT_READ = 17; +// public static final int ALLOW_NORMAL_READ = 4; +// public static final int ALLOW_NORMAL_WRITE = 5; +// public static final int ALLOW_REF_DATA_READ = 21; +// public static final int ALLOW_REF_DATA_WRITE = 19; +// public static final int ALLOW_TEXCOORD_READ = 6; +// public static final int ALLOW_TEXCOORD_WRITE = 7; + + public static final int BY_REFERENCE = 128; + public static final int COLOR_3 = 4; + public static final int COLOR_4 = 12; + public static final int COORDINATES = 1; + public static final int INTERLEAVED = 256; + public static final int NORMALS = 2; + public static final int TEXTURE_COORDINATE_2 = 32; + public static final int TEXTURE_COORDINATE_3 = 64; + public static final int TEXTURE_COORDINATE_4 = 1024; + public static final int USE_COORD_INDEX_ONLY = 512; + public static final int USE_NIO_BUFFER = 2048; + + protected int vertexCount; + protected int vertexFormat; + protected FloatBuffer vertexBuffer = null; + protected FloatBuffer normalBuffer = null; + protected FloatBuffer uvBuffer = null; + + // コンストラクタ + public GeometryArray(int vertexCount, int vertexFormat) { + this.vertexCount = vertexCount; + this.vertexFormat = vertexFormat; + ByteBuffer vbb = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb.order(ByteOrder.nativeOrder()); + vertexBuffer = vbb.asFloatBuffer(); + if ((vertexFormat & NORMALS) != 0) { + ByteBuffer vbb2 = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb2.order(ByteOrder.nativeOrder()); + normalBuffer = vbb2.asFloatBuffer(); + } + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 2); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 4); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } +// mVertexBuffer.put(vertices); +// mVertexBuffer.position(0); + } + +// public GeometryArray(int vertexCount, int vertexFormat, int texCoordSetCount, int[] texCoordSetMap) { +// this.vertexCount = vertexCount; +// this.vertexFormat = vertexFormat; +// this.texCoordSetCount = texCoordSetCount; +// this.texCoordSetMap = texCoordSetMap; +// } + + /** オブジェクトの頂点数 */ + public int getVertexCount() { + return vertexCount; + } + + public int getVertexFormat() { + return vertexFormat; + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.get(p); + vertexBuffer.position(0); + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, double[] p) { + p[0] = vertexBuffer.get(index * 3); + p[1] = vertexBuffer.get(index * 3 + 1); + p[2] = vertexBuffer.get(index * 3 + 2); + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, Point3d p) { + p.x = vertexBuffer.get(index * 3); + p.y = vertexBuffer.get(index * 3 + 1); + p.z = vertexBuffer.get(index * 3 + 2); + } + + public void getCoordinates(int index, double[] coordinates) { + for (int n = 0; n < coordinates.length / 3 && n + index < vertexBuffer.capacity() / 3; n++) { + coordinates[n * 3] = vertexBuffer.get((index + n) * 3); + coordinates[n * 3 + 1] = vertexBuffer.get((index + n) * 3 + 1); + coordinates[n * 3 + 2] = vertexBuffer.get((index + n) * 3 + 2); + } + } + + /** indexの頂点の法線をpに新しく格納する */ + public void getNormal(int index, float[] p) { + if (normalBuffer == null) return; + normalBuffer .position(index * 3); + normalBuffer.get(p); + normalBuffer .position(0); + } + + /** indexの頂点の法線をpに新しく格納する */ + public void getNormal(int index, Vector3f p) { + if (normalBuffer == null) return; + p.x = normalBuffer.get(index * 3); + p.y = normalBuffer.get(index * 3 + 1); + p.z = normalBuffer.get(index * 3 + 2); + } + + public void setCoordinate(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put(p); + vertexBuffer.position(0); + } + + public void setCoordinate(int index, double[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put((float)p[0]); + vertexBuffer.put((float)p[1]); + vertexBuffer.put((float)p[2]); + vertexBuffer.position(0); + } + + public void setCoordinate(int index, Point3d p) { + vertexBuffer.position(index * 3); + vertexBuffer.put((float)p.x); + vertexBuffer.put((float)p.y); + vertexBuffer.put((float)p.z); + vertexBuffer.position(0); + } + + public void setCoordinates(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put(p); + vertexBuffer.position(0); + } + + public void setCoordinates(int index, double[] p) { + vertexBuffer.position(index * 3); + for (int i = 0; i < p.length; i++) { + vertexBuffer.put((float)p[i]); + } + vertexBuffer.position(0); + } + + public void setNormal(int index, float[] n) { + if (normalBuffer == null) return; + normalBuffer .position(index * 3); + normalBuffer.put(n); + normalBuffer.position(0); + } + + public void setNormal(int index, Vector3f n) { + if (normalBuffer == null) return; + normalBuffer.position(index * 3); + normalBuffer.put(n.x); + normalBuffer.put(n.y); + normalBuffer.put(n.z); + normalBuffer.position(0); + } + + public void setTextureCoordinate(int index, float[] texCoord) { + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + uvBuffer.position(index * 2); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + uvBuffer.position(index * 3); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + uvBuffer.put(texCoord[2]); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + uvBuffer.position(index * 4); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + uvBuffer.put(texCoord[2]); + uvBuffer.put(texCoord[3]); + } + uvBuffer.position(0); + } + + public void setTextureCoordinates(int index, float[] texCoords) { + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 2); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 2); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 3); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 3); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 4); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 4); + } + uvBuffer.put(texCoords); + uvBuffer.position(0); + } + + public FloatBuffer getVertexBuffer() { + return vertexBuffer; + } + + public FloatBuffer getNormalBuffer() { + return normalBuffer; + } + + public FloatBuffer getUVBuffer() { + return uvBuffer; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryStripArray.java new file mode 100644 index 0000000..80d7002 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GeometryStripArray.java @@ -0,0 +1,22 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class GeometryStripArray extends GeometryArray { + protected int[] stripIndexCounts; + + public GeometryStripArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat); + this.stripIndexCounts = stripIndexCounts; + } + + public int getNumStrips() { + return stripIndexCounts.length; + } + + public void setStripIndexCounts(int[] stripIndexCounts) { + this.stripIndexCounts = stripIndexCounts; + } + + public void getStripVertexCounts(int[] stripIndexCounts) { + System.arraycopy(this.stripIndexCounts, 0, stripIndexCounts, 0, stripIndexCounts.length); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GraphicsContext3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GraphicsContext3D.java new file mode 100644 index 0000000..572f361 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/GraphicsContext3D.java @@ -0,0 +1,378 @@ +package com.google.ar.core.examples.java.common.java3d; + +import android.opengl.GLU; +import android.opengl.GLUtils; + +import com.google.ar.core.examples.java.common.framework.model3D.Position3D; + +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.HashMap; + +import javax.microedition.khronos.opengles.GL10; + +public class GraphicsContext3D { + private GL10 gl; + private Appearance appearance = null; + private boolean bFixAspect = false; + private float aspect; + private HashMap textureRegistry = new HashMap(); + private HashMap lightRegistry = new HashMap(); + + public GraphicsContext3D(GL10 gl) { + init(gl); + } + + public GraphicsContext3D setGL10(GL10 gl) { + if (this.gl != gl) { + init(gl); + } + return this; + } + + public void fixAspect(float aspect) { + this.bFixAspect = true; + this.aspect = aspect; + } + + public void init(GL10 gl) { + this.gl = gl; + // デプスバッファのテスト機能を有効にする + gl.glEnable(GL10.GL_DEPTH_TEST); + // 陰面消去の動作を設定 + gl.glDepthFunc(GL10.GL_LEQUAL); + gl.glDepthMask(true); + + // ライトを有効にする + gl.glEnable(GL10.GL_LIGHTING); + // どの光源を使用するか指定 + gl.glEnable(GL10.GL_LIGHT0); + + gl.glClearColor(0.0f,0.0f,0.0f,0.0f); + gl.glClearDepthf(1.0f); + } + + public void update(int width, int height, float fovx, float zNear, float zFar,boolean fParallel) { + setGL10(gl); + if (!bFixAspect) { + aspect = (float)width / (float)height; + } + // ビューポートの設定 + gl.glViewport(0, 0, width, height); + + // カメラの設定 + gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換 + gl.glLoadIdentity(); // 座標の初期化 + // 画角の設定 + float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f); + if(!fParallel){ + GLU.gluPerspective(gl, + fovy, //Y方向の画角 + aspect, //アスペクト比 + zNear, //ニアクリップ + zFar);//ファークリップ + }else{ + float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + gl.glOrthof(left, right, bottom, top, zNear, zFar); + } + } + + public void update(float fovx, float zNear, float zFar, Position3D eye, Position3D center, Vector3d up, boolean fParallel) { + // 表示画面とデプスバッファのクリア + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + + // カメラの設定 + gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換 + gl.glLoadIdentity(); // 座標の初期化 + // 画角の設定 + float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f); + if (!fParallel) { + GLU.gluPerspective(gl, + fovy, //Y方向の画角 + aspect, //アスペクト比 + zNear, //ニアクリップ + zFar);//ファークリップ + } else { + float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + gl.glOrthof(left, right, bottom, top, zNear, zFar); + } + // モデルビュー行列の指定 + gl.glMatrixMode(GL10.GL_MODELVIEW); + // 座標の初期化 + gl.glLoadIdentity(); + + // カメラ外部パラメータの設定 + GLU.gluLookAt(gl, + (float)eye.getX(), (float)eye.getY(), (float)eye.getZ(), + (float)center.getX(), (float)center.getY(), (float)center.getZ(), + (float)up.getX(), (float)up.getY(), (float)up.getZ()); + } + + public void setLight(Light l, int i) { + lightRegistry.put(l, i); + Color3f c = l.getColor(); + float color[] = {c.r, c.g, c.b, 1.0f}; + gl.glEnable(GL10.GL_LIGHT0 + i); + if (l instanceof AmbientLight) { + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, color, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + } else { + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, color, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{1.0f, 1.0f, 1.0f, 1.0f}, 0); + } + } + + public void updateLightState(Light l) { + Integer i = lightRegistry.get(l); + if (i == null) { + // Object3D内部に配置されている光源に対しては、初期化時にsetLight()が呼ばれないため + i = lightRegistry.size(); + setLight(l, i); + } + if (l instanceof DirectionalLight) { + Vector3f v = ((DirectionalLight)l).getDirection(); + float direction[] = {-v.x, -v.y, -v.z, 0.0f}; + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, direction, 0); + } else if (l instanceof PointLight) { + Point3f p = ((PointLight)l).getPosition(); + float position[] = {p.x, p.y, p.z, 1.0f}; + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, position, 0); + } + } + + public void pushMatrix() { + gl.glPushMatrix(); + } + + public void popMatrix() { + gl.glPopMatrix(); + } + + public void multMatrix(Transform3D transform) { + float m[] = transform.getMatrix(); + gl.glMultMatrixf(m, 0); + } + + public void setAppearance(Appearance appearance) { + this.appearance = appearance; + Material material = appearance.getMaterial(); + if (material != null) { + // 表面属性の設定 + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_DIFFUSE, material.diffuse, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_AMBIENT, material.ambient, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_SPECULAR, material.specular, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_EMISSION, material.emissive, 0); + gl.glMaterialf(GL10.GL_FRONT, GL10.GL_SHININESS, material.shininess); + } + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + Texture tex = appearance.getTexture(); + if (tex != null) registerTexture(tex); + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + TextureUnitState tus = appearance.getTextureUnitState(n); + Texture tex = tus.getTexture(); + if (tex != null) { + gl.glActiveTexture(GL10.GL_TEXTURE1 + n); + registerTexture(tex); + } + } + } + } + + private void registerTexture(Texture tex) { + if (textureRegistry.get(tex) == null) { + // テクスチャの登録 + int[] textureId = new int[1]; + gl.glGenTextures(1, textureId, 0); + if (tex instanceof TextureCubeMap) { + // 立方体マップの場合(GL10では対応していない) + } else { + // 通常の場合 + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId[0]); + ImageComponent[] imageComponents = tex.getImages(); + for (int level = 0; level < imageComponents.length; level++) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, ((ImageComponent2D)imageComponents[level]).getBitmap(), 0); +// ((ImageComponent2D)imageComponents[i]).getBitmap().recycle(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); + } + } + textureRegistry.put(tex, textureId[0]); + } + } + + public void draw(Shape3D node) { + if (node == null) return; + setAppearance(node.getAppearance()); + draw(node.getGeometry()); + } + + public void draw(Geometry g) { + if (g == null) return; + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + Texture tex = appearance.getTexture(); + if (tex != null) { + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提 + TextureAttributes ta = appearance.getTextureAttributes(); + if (ta != null) setTextureAttributes(ta); + } + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + TextureUnitState tus = appearance.getTextureUnitState(n); + Texture tex = tus.getTexture(); + if (tex != null) { + gl.glActiveTexture(GL10.GL_TEXTURE0 + n); + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提 + TextureAttributes ta = tus.getTextureAttributes(); + if (ta != null) setTextureAttributes(ta); + } + } + } + if (g instanceof GeometryArray) { + // バッファの設定 + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + FloatBuffer vertexBuffer = ((GeometryArray)g).getVertexBuffer(); + gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + FloatBuffer normalBuffer = ((GeometryArray)g).getNormalBuffer(); + gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer); + } + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, uvBuffer); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, uvBuffer); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(4, GL10.GL_FLOAT, 0, uvBuffer); + } + + // ジオメトリの描画 + if (g instanceof TriangleArray) { + TriangleArray ta = (TriangleArray)g; + int vertexCount = ta.getVertexCount(); + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexCount); + } else if (g instanceof TriangleFanArray) { + TriangleFanArray ta = (TriangleFanArray)g; + int start = 0; + int[] stripVertexCounts = new int[ta.getNumStrips()]; + ta.getStripVertexCounts(stripVertexCounts); + for (int n = 0; n < ta.getNumStrips(); n++) { + int vertexCount = stripVertexCounts[n]; + gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, start, vertexCount); + start += vertexCount; + } + } else if (g instanceof TriangleStripArray) { + TriangleStripArray ta = (TriangleStripArray)g; + int start = 0; + int[] stripVertexCounts = new int[ta.getNumStrips()]; + ta.getStripVertexCounts(stripVertexCounts); + for (int n = 0; n < ta.getNumStrips(); n++) { + int vertexCount = stripVertexCounts[n]; + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, start, vertexCount); + start += vertexCount; + } + } else if (g instanceof IndexedTriangleArray) { + IndexedTriangleArray ita = (IndexedTriangleArray)g; + int vertexCount = ((IndexedTriangleArray)g).getIndexCount(); + gl.glDrawElements(GL10.GL_TRIANGLES, vertexCount, GL10.GL_UNSIGNED_SHORT, ita.getCoordinateIndexBuffer()); + } else if (g instanceof IndexedTriangleFanArray) { + IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g; + ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer(); + int start = 0; + int[] stripIndexCounts = new int[igsa.getNumStrips()]; + igsa.getStripIndexCounts(stripIndexCounts); + for (int n = 0; n < igsa.getNumStrips(); n++) { + int vertexCount = stripIndexCounts[n]; + indexBuffer.position(start); + gl.glDrawElements(GL10.GL_TRIANGLE_FAN, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer); + start += vertexCount; + } + } else if (g instanceof IndexedTriangleStripArray) { + IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g; + ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer(); + int start = 0; + int[] stripIndexCounts = new int[igsa.getNumStrips()]; + igsa.getStripIndexCounts(stripIndexCounts); + for (int n = 0; n < igsa.getNumStrips(); n++) { + int vertexCount = stripIndexCounts[n]; + indexBuffer.position(start); + gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer); + start += vertexCount; + } + } + + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) { + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + } + gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); + } + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + if (appearance.getTexture() != null) { + gl.glDisable(GL10.GL_TEXTURE_2D); + } + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + gl.glActiveTexture(GL10.GL_TEXTURE0 + n); + gl.glDisable(GL10.GL_TEXTURE_2D); + } + gl.glActiveTexture(GL10.GL_TEXTURE0); + } + } + + private void setTextureAttributes(TextureAttributes ta) { + int textureMode = ta.getTextureMode(); + switch (textureMode) { + case TextureAttributes.REPLACE: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); + break; + case TextureAttributes.BLEND: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_BLEND); + break; +// case TextureAttributes.COMBINE: +// gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_COMBINE); +// break; + case TextureAttributes.MODULATE: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); + break; + case TextureAttributes.DECAL: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL); + break; + } +// int perspCorrectionMode = ta.getPerspectiveCorrectionMode(); +// switch (perspCorrectionMode) { +// case TextureAttributes.NICEST: +// break; +// case TextureAttributes.FASTEST: +// break; +// } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Group.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Group.java new file mode 100644 index 0000000..ca278d7 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Group.java @@ -0,0 +1,40 @@ +package com.google.ar.core.examples.java.common.java3d; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +public class Group extends Node { + + private List children = new ArrayList(); + + public Node getChild(int num) { + return children.get(num); + } + + public int numChildren() { + return children.size(); + } + + public void addChild(Node node) { + children.add(node); + } + + public void removeChild(Node node) { + children.remove(node); + } + + public Enumeration getAllChildren() { + return Collections.enumeration(children); + } + + public Node cloneTree() { + Group newInstance = new Group(); + newInstance.children = new ArrayList(); + for (int n = 0; n < children.size(); n++) { + newInstance.children.add(children.get(n).cloneTree()); + } + return newInstance; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java new file mode 100644 index 0000000..9c00a74 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent.java @@ -0,0 +1,16 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class ImageComponent extends NodeComponent { + public static final int FORMAT_RGB = 1; + public static final int FORMAT_RGBA = 2; + + protected int format = 0; + protected int width = 0; + protected int height = 0; + + public ImageComponent(int format, int width, int height) { + this.format = format; + this.width = width; + this.height = height; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java new file mode 100644 index 0000000..1235eae --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/ImageComponent2D.java @@ -0,0 +1,23 @@ +package com.google.ar.core.examples.java.common.java3d; + +import android.graphics.Bitmap; + +public class ImageComponent2D extends ImageComponent { + private Bitmap image = null; + + public ImageComponent2D(int format, Bitmap image) { + super(format, image.getWidth(), image.getHeight()); + this.image = image; + } + + public Bitmap getBitmap() { + return image; + } + + @Override + public NodeComponent cloneNodeComponent() { + ImageComponent2D newOne = new ImageComponent2D(format, image); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java new file mode 100644 index 0000000..ba9aa40 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryArray.java @@ -0,0 +1,89 @@ +package com.google.ar.core.examples.java.common.java3d; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +public abstract class IndexedGeometryArray extends GeometryArray { + + protected int indexCount; + protected ShortBuffer coordinateIndexBuffer = null; + protected ShortBuffer texCoordinateIndexBuffer = null; + protected ShortBuffer normalIndexBuffer = null; + + // コンストラクタ + public IndexedGeometryArray(int vertexCount, int vertexFormat, int indexCount) { + super(vertexCount, vertexFormat); + this.indexCount = indexCount; + ByteBuffer vbb = ByteBuffer.allocateDirect(indexCount * 2); + vbb.order(ByteOrder.nativeOrder()); + coordinateIndexBuffer = vbb.asShortBuffer(); + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0 + || (vertexFormat & TEXTURE_COORDINATE_3) != 0 + || (vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb2 = ByteBuffer.allocateDirect(indexCount * 2); + vbb2.order(ByteOrder.nativeOrder()); + texCoordinateIndexBuffer = vbb2.asShortBuffer(); + } + if ((vertexFormat & NORMALS) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(indexCount * 2); + vbb3.order(ByteOrder.nativeOrder()); + normalIndexBuffer = vbb3.asShortBuffer(); + } + } + + /** オブジェクトのインデックス数を取得する */ + public int getIndexCount() { + return indexCount; + } + + public int getCoordinateIndex(int index) { + return coordinateIndexBuffer.get(index); + } + + public void getCoordinateIndices(int index, int[] coordinateIndicies) { + for (int i = 0; i < coordinateIndicies.length && index + i < coordinateIndexBuffer.limit(); i++) { + coordinateIndicies[i] = coordinateIndexBuffer.get(index + i); + } + } + + public void setCoordinateIndex(int index, int coordinateIndex) { + coordinateIndexBuffer.put(index, (short)coordinateIndex); + } + + public void setCoordinateIndices(int index, int[] coordinateIndicies) { + coordinateIndexBuffer.position(index); + for (int i = 0; i < coordinateIndicies.length; i++) { + coordinateIndexBuffer.put((short)coordinateIndicies[i]); + } + coordinateIndexBuffer.position(0); + } + + public int getTextureCoordinateIndex(int index) { + return texCoordinateIndexBuffer.get(index); + } + + public void setTextureCoordinateIndex(int index, int texCoordIndex) { + coordinateIndexBuffer.put(index, (short)texCoordIndex); + } + + public int getNormalIndex(int index) { + return normalIndexBuffer.get(index); + } + + public void setNormalIndex(int index, int normalIndex) { + normalIndexBuffer.put(index, (short)normalIndex); + } + + public ShortBuffer getCoordinateIndexBuffer() { + return coordinateIndexBuffer; + } + + public ShortBuffer getTextureCoordinateIndexBuffer() { + return texCoordinateIndexBuffer; + } + + public ShortBuffer getNormalIndexBuffer() { + return normalIndexBuffer; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java new file mode 100644 index 0000000..682bfad --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedGeometryStripArray.java @@ -0,0 +1,22 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class IndexedGeometryStripArray extends IndexedGeometryArray { + protected int[] stripIndexCounts; + + public IndexedGeometryStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount); + this.stripIndexCounts = stripIndexCounts; + } + + public int getNumStrips() { + return stripIndexCounts.length; + } + + public void setStripIndexCounts(int[] stripIndexCounts) { + this.stripIndexCounts = stripIndexCounts; + } + + public void getStripIndexCounts(int[] stripIndexCounts) { + System.arraycopy(this.stripIndexCounts, 0, stripIndexCounts, 0, stripIndexCounts.length); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java new file mode 100644 index 0000000..a2d751c --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleArray.java @@ -0,0 +1,21 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class IndexedTriangleArray extends IndexedGeometryArray { + public IndexedTriangleArray(int vertexCount, int vertexFormat, int indexCount) { + super(vertexCount, vertexFormat, indexCount); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleArray newOne = new IndexedTriangleArray(vertexCount, vertexFormat, indexCount); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java new file mode 100644 index 0000000..52556e0 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleFanArray.java @@ -0,0 +1,21 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class IndexedTriangleFanArray extends IndexedGeometryStripArray { + public IndexedTriangleFanArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedGeometryArray newOne = new IndexedTriangleFanArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java new file mode 100644 index 0000000..b9a9739 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/IndexedTriangleStripArray.java @@ -0,0 +1,20 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class IndexedTriangleStripArray extends IndexedGeometryStripArray { + public IndexedTriangleStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleStripArray newOne = new IndexedTriangleStripArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java new file mode 100644 index 0000000..91950b9 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Leaf.java @@ -0,0 +1,5 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Leaf extends Node { + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java new file mode 100644 index 0000000..a20b4c1 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Light.java @@ -0,0 +1,17 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Light extends Leaf { + protected Color3f color; + + public Light(Color3f c) { + this.color = c; + } + + public void setColor(Color3f c) { + color = c; + } + + public Color3f getColor() { + return color; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java new file mode 100644 index 0000000..f8c426b --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Material.java @@ -0,0 +1,73 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Material extends NodeComponent { + float diffuseR = 1.0f; + float diffuseG = 1.0f; + float diffuseB = 1.0f; + float ambientR = 0.2f; + float ambientG = 0.2f; + float ambientB = 0.2f; + float specularR = 1.0f; + float specularG = 1.0f; + float specularB = 1.0f; + float emissiveR = 0.0f; + float emissiveG = 0.0f; + float emissiveB = 0.0f; + float shininess = 64.0f; + float[] diffuse = null; + float[] ambient = null; + float[] specular = null; + float[] emissive = null; + + public Material() { + setDiffuseColor(1.0f, 1.0f, 1.0f); + setAmbientColor(0.2f, 0.2f, 0.2f); + setSpecularColor(1.0f, 1.0f, 1.0f); + setEmissiveColor(0.0f, 0.0f, 0.0f); + setShininess(64.0f); + } + + public void setDiffuseColor(float r, float g, float b) { + this.diffuseR = r; + this.diffuseG = g; + this.diffuseB = b; + diffuse = new float[]{r, g ,b, 1.0f}; + } + + public void setAmbientColor(float r, float g, float b) { + this.ambientR = r; + this.ambientG = g; + this.ambientB = b; + ambient = new float[]{r, g ,b, 1.0f}; + } + + public void setSpecularColor(float r, float g, float b) { + this.specularR = r; + this.specularG = g; + this.specularB = b; + specular = new float[]{r, g ,b, 1.0f}; + } + + public void setEmissiveColor(float r, float g, float b) { + this.emissiveR = r; + this.emissiveG = g; + this.emissiveB = b; + emissive = new float[]{r, g ,b, 1.0f}; + } + + public void setShininess(float shininess) { + this.shininess = shininess; + } + + @Override + public NodeComponent cloneNodeComponent() { + Material m = new Material(); + m.setDiffuseColor(diffuseR, diffuseG, diffuseB); + m.setAmbientColor(ambientR, ambientG, ambientB); + m.setSpecularColor(specularR, specularG, specularB); + m.setEmissiveColor(emissiveR, emissiveG, emissiveB); + m.setShininess(shininess); + return m; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java new file mode 100644 index 0000000..017eaf8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix3d.java @@ -0,0 +1,484 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Matrix3d { + public double m00; + public double m01; + public double m02; + public double m10; + public double m11; + public double m12; + public double m20; + public double m21; + public double m22; + + // コンストラクタ + public Matrix3d() { + m00 = 0.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 0.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 0.0; + } + + public Matrix3d(double[] v) { + m00 = v[0]; + m01 = v[1]; + m02 = v[2]; + m10 = v[3]; + m11 = v[4]; + m12 = v[5]; + m20 = v[6]; + m21 = v[7]; + m22 = v[8]; + } + + public Matrix3d(double m00, double m01, double m02, double m10, double m11, + double m12, double m20, double m21, double m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + } + + public Matrix3d(Matrix3d m1) { + this.m00 = m1.m00; + this.m01 = m1.m01; + this.m02 = m1.m02; + this.m10 = m1.m10; + this.m11 = m1.m11; + this.m12 = m1.m12; + this.m20 = m1.m20; + this.m21 = m1.m21; + this.m22 = m1.m22; + } + + public Matrix3d clone() { + return new Matrix3d(m00, m01, m02, m10, m11, m12, m20, m21, m22); + } + + /** 各要素に足す */ + public void add(Matrix3d m1) { + this.m00 += m1.m00; + this.m01 += m1.m01; + this.m02 += m1.m02; + this.m10 += m1.m10; + this.m11 += m1.m11; + this.m12 += m1.m12; + this.m20 += m1.m20; + this.m21 += m1.m21; + this.m22 += m1.m22; + } + + /** 二つの行列の和を格納する */ + public void add(Matrix3d m1, Matrix3d m2) { + this.m00 = m1.m00 + m2.m00; + this.m01 = m1.m01 + m2.m01; + this.m02 = m1.m02 + m2.m02; + this.m10 = m1.m10 + m2.m10; + this.m11 = m1.m11 + m2.m11; + this.m12 = m1.m12 + m2.m12; + this.m20 = m1.m20 + m2.m20; + this.m21 = m1.m21 + m2.m21; + this.m22 = m1.m22 + m2.m22; + } + + /** x軸周りにangle回転 */ + public void rotX(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = 1.0D; + m01 = 0.0D; + m02 = 0.0D; + m10 = 0.0D; + m11 = d2; + m12 = -d1; + m20 = 0.0D; + m21 = d1; + m22 = d2; + } + + /** y軸周りにangle回転 */ + public void rotY(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = d2; + m01 = 0.0D; + m02 = d1; + m10 = 0.0D; + m11 = 1.0D; + m12 = 0.0D; + m20 = -d1; + m21 = 0.0D; + m22 = d2; + } + + /** z軸周りにangle1回転 */ + public void rotZ(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = d2; + m01 = -d1; + m02 = 0.0D; + m10 = d1; + m11 = d2; + m12 = 0.0D; + m20 = 0.0D; + m21 = 0.0D; + m22 = 1.0D; + } + + /** 自分に引数の行列を掛けたものを格納する */ + public void mul(Matrix3d mat) { + double t00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20; + double t01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21; + double t02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22; + double t10 = m10 * mat.m10 + m11 * mat.m10 + m12 * mat.m20; + double t11 = m10 * mat.m11 + m11 * mat.m11 + m12 * mat.m21; + double t12 = m10 * mat.m12 + m11 * mat.m12 + m12 * mat.m22; + double t20 = m20 * mat.m20 + m21 * mat.m10 + m22 * mat.m20; + double t21 = m20 * mat.m21 + m21 * mat.m11 + m22 * mat.m21; + double t22 = m20 * mat.m22 + m21 * mat.m12 + m22 * mat.m22; + + set(t00, t01, t02, t10, t11, t12, t20, t21, t22); + } + + public void mul(Matrix3d mat1, Matrix3d mat2) { + this.set(mat1.m00, mat1.m01, mat1.m02, mat1.m10, mat1.m11, mat1.m12, mat1.m20, mat1.m21, mat1.m22); + mul(mat2); + } + + public void set(double t00, double t01, double t02, double t10, double t11, + double t12, double t20, double t21, double t22) { + m00 = t00; + m01 = t01; + m02 = t02; + m10 = t10; + m11 = t11; + m12 = t12; + m20 = t20; + m21 = t21; + m22 = t22; + } + + /** + * Sets the value of this matrix to the matrix inverse of the passed matrix + * m1. + * + * @param m1 + * the matrix to be inverted + */ + public final void invert(Matrix3d m1) { + invertGeneral(m1); + } + + /** + * Inverts this matrix in place. + */ + public final void invert() { + invertGeneral(this); + } + + /** + * General invert routine. Inverts m1 and places the result in "this". Note + * that this routine handles both the "this" version and the non-"this" + * version. + * + * Also note that since this routine is slow anyway, we won't worry about + * allocating a little bit of garbage. + */ + private final void invertGeneral(Matrix3d m1) { + double result[] = new double[9]; + int row_perm[] = new int[3]; + int i, r, c; + double[] tmp = new double[9]; // scratch matrix + + // Use LU decomposition and backsubstitution code specifically + // for floating-point 3x3 matrices. + + // Copy source matrix to t1tmp + tmp[0] = m1.m00; + tmp[1] = m1.m01; + tmp[2] = m1.m02; + + tmp[3] = m1.m10; + tmp[4] = m1.m11; + tmp[5] = m1.m12; + + tmp[6] = m1.m20; + tmp[7] = m1.m21; + tmp[8] = m1.m22; + + // Calculate LU decomposition: Is the matrix singular? + if (!luDecomposition(tmp, row_perm)) { + // Matrix has no inverse + // throw new + // SingularMatrixException(VecMathI18N.getString("Matrix3d12")); + } + + // Perform back substitution on the identity matrix + for (i = 0; i < 9; i++) + result[i] = 0.0; + result[0] = 1.0; + result[4] = 1.0; + result[8] = 1.0; + luBacksubstitution(tmp, row_perm, result); + + this.m00 = result[0]; + this.m01 = result[1]; + this.m02 = result[2]; + + this.m10 = result[3]; + this.m11 = result[4]; + this.m12 = result[5]; + + this.m20 = result[6]; + this.m21 = result[7]; + this.m22 = result[8]; + + } + + /** + * Given a 3x3 array "matrix0", this function replaces it with the LU + * decomposition of a row-wise permutation of itself. The input parameters + * are "matrix0" and "dimen". The array "matrix0" is also an output + * parameter. The vector "row_perm[3]" is an output parameter that contains + * the row permutations resulting from partial pivoting. The output + * parameter "even_row_xchg" is 1 when the number of row exchanges is even, + * or -1 otherwise. Assumes data type is always double. + * + * This function is similar to luDecomposition, except that it is tuned + * specifically for 3x3 matrices. + * + * @return true if the matrix is nonsingular, or false otherwise. + */ + // + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 40-45. + // + static boolean luDecomposition(double[] matrix0, int[] row_perm) { + + double row_scale[] = new double[3]; + + // Determine implicit scaling information by looping over rows + { + int i, j; + int ptr, rs; + double big, temp; + + ptr = 0; + rs = 0; + + // For each row ... + i = 3; + while (i-- != 0) { + big = 0.0; + + // For each column, find the largest element in the row + j = 3; + while (j-- != 0) { + temp = matrix0[ptr++]; + temp = Math.abs(temp); + if (temp > big) { + big = temp; + } + } + + // Is the matrix singular? + if (big == 0.0) { + return false; + } + row_scale[rs++] = 1.0 / big; + } + } + + { + int j; + int mtx; + + mtx = 0; + + // For all columns, execute Crout's method + for (j = 0; j < 3; j++) { + int i, imax, k; + int target, p1, p2; + double sum, big, temp; + + // Determine elements of upper diagonal matrix U + for (i = 0; i < j; i++) { + target = mtx + (3 * i) + j; + sum = matrix0[target]; + k = i; + p1 = mtx + (3 * i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 3; + } + matrix0[target] = sum; + } + + // Search for largest pivot element and calculate + // intermediate elements of lower diagonal matrix L. + big = 0.0; + imax = -1; + for (i = j; i < 3; i++) { + target = mtx + (3 * i) + j; + sum = matrix0[target]; + k = j; + p1 = mtx + (3 * i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 3; + } + matrix0[target] = sum; + + // Is this the best pivot so far? + if ((temp = row_scale[i] * Math.abs(sum)) >= big) { + big = temp; + imax = i; + } + } + + if (imax < 0) { + // throw new + // RuntimeException(VecMathI18N.getString("Matrix3d13")); + } + + // Is a row exchange necessary? + if (j != imax) { + // Yes: exchange rows + k = 3; + p1 = mtx + (3 * imax); + p2 = mtx + (3 * j); + while (k-- != 0) { + temp = matrix0[p1]; + matrix0[p1++] = matrix0[p2]; + matrix0[p2++] = temp; + } + + // Record change in scale factor + row_scale[imax] = row_scale[j]; + } + + // Record row permutation + row_perm[j] = imax; + + // Is the matrix singular + if (matrix0[(mtx + (3 * j) + j)] == 0.0) { + return false; + } + + // Divide elements of lower diagonal matrix L by pivot + if (j != (3 - 1)) { + temp = 1.0 / (matrix0[(mtx + (3 * j) + j)]); + target = mtx + (3 * (j + 1)) + j; + i = 2 - j; + while (i-- != 0) { + matrix0[target] *= temp; + target += 3; + } + } + } + } + + return true; + } + + /** + * Solves a set of linear equations. The input parameters "matrix1", and + * "row_perm" come from luDecompostionD3x3 and do not change here. The + * parameter "matrix2" is a set of column vectors assembled into a 3x3 + * matrix of floating-point values. The procedure takes each column of + * "matrix2" in turn and treats it as the right-hand side of the matrix + * equation Ax = LUx = b. The solution vector replaces the original column + * of the matrix. + * + * If "matrix2" is the identity matrix, the procedure replaces its contents + * with the inverse of the matrix from which "matrix1" was originally + * derived. + */ + // + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 44-45. + // + static void luBacksubstitution(double[] matrix1, int[] row_perm, + double[] matrix2) { + + int i, ii, ip, j, k; + int rp; + int cv, rv; + + // rp = row_perm; + rp = 0; + + // For each column vector of matrix2 ... + for (k = 0; k < 3; k++) { + // cv = &(matrix2[0][k]); + cv = k; + ii = -1; + + // Forward substitution + for (i = 0; i < 3; i++) { + double sum; + + ip = row_perm[rp + i]; + sum = matrix2[cv + 3 * ip]; + matrix2[cv + 3 * ip] = matrix2[cv + 3 * i]; + if (ii >= 0) { + // rv = &(matrix1[i][0]); + rv = i * 3; + for (j = ii; j <= i - 1; j++) { + sum -= matrix1[rv + j] * matrix2[cv + 3 * j]; + } + } else if (sum != 0.0) { + ii = i; + } + matrix2[cv + 3 * i] = sum; + } + + // Backsubstitution + // rv = &(matrix1[3][0]); + rv = 2 * 3; + matrix2[cv + 3 * 2] /= matrix1[rv + 2]; + + rv -= 3; + matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2] + * matrix2[cv + 3 * 2]) + / matrix1[rv + 1]; + + rv -= 3; + matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1] + * matrix2[cv + 3 * 1] - matrix1[rv + 2] + * matrix2[cv + 3 * 2]) + / matrix1[rv + 0]; + + } + } + + /** + * Multiply this matrix by the tuple t and place the result + * back into the tuple (t = this*t). + * @param t the tuple to be multiplied by this matrix and then replaced + */ + public final void transform(Tuple3d t) { + double x,y,z; + x = m00* t.x + m01*t.y + m02*t.z; + y = m10* t.x + m11*t.y + m12*t.z; + z = m20* t.x + m21*t.y + m22*t.z; + t.set(x,y,z); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java new file mode 100644 index 0000000..3c9e1b5 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Matrix4d.java @@ -0,0 +1,216 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Matrix4d { + public double m00; + public double m01; + public double m02; + public double m03; + public double m10; + public double m11; + public double m12; + public double m13; + public double m20; + public double m21; + public double m22; + public double m23; + public double m30; + public double m31; + public double m32; + public double m33; + + // コンストラクタ + public Matrix4d() { + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m03 = 0.0; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m13 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + m23 = 0.0; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + m33 = 1.0; + } + + public Matrix4d(double[] v) { + m00 = v[0]; + m01 = v[1]; + m02 = v[2]; + m03 = v[3]; + m10 = v[4]; + m11 = v[5]; + m12 = v[6]; + m13 = v[7]; + m20 = v[8]; + m21 = v[9]; + m22 = v[10]; + m23 = v[11]; + m30 = v[12]; + m31 = v[13]; + m32 = v[14]; + m33 = v[15]; + } + + public Matrix4d(double m00, double m01, double m02, double m03, double m10, + double m11, double m12, double m13, double m20, double m21, + double m22, double m23, double m30, double m31, double m32, + double m33) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m03 = m03; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + } + + public Matrix4d clone() { + return new Matrix4d(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33); + } + + public Matrix4d(Matrix4d m1) { + this.m00 = m1.m00; + this.m01 = m1.m01; + this.m02 = m1.m02; + this.m03 = m1.m03; + this.m10 = m1.m10; + this.m11 = m1.m11; + this.m12 = m1.m12; + this.m13 = m1.m13; + this.m20 = m1.m20; + this.m21 = m1.m21; + this.m22 = m1.m22; + this.m23 = m1.m23; + this.m30 = m1.m30; + this.m31 = m1.m31; + this.m32 = m1.m32; + this.m33 = m1.m33; + } + + /** 各要素に足す */ + public void add(Matrix4d m1) { + this.m00 += m1.m00; + this.m01 += m1.m01; + this.m02 += m1.m02; + this.m03 += m1.m03; + this.m10 += m1.m10; + this.m11 += m1.m11; + this.m12 += m1.m12; + this.m13 += m1.m13; + this.m20 += m1.m20; + this.m21 += m1.m21; + this.m22 += m1.m22; + this.m23 += m1.m23; + this.m30 += m1.m30; + this.m31 += m1.m31; + this.m32 += m1.m32; + this.m33 += m1.m33; + } + + /** 二つの行列の和を格納する */ + public void add(Matrix4d m1, Matrix4d m2) { + this.m00 = m1.m00 + m2.m00; + this.m01 = m1.m01 + m2.m01; + this.m02 = m1.m02 + m2.m02; + this.m03 = m1.m03 + m2.m03; + this.m10 = m1.m10 + m2.m10; + this.m11 = m1.m11 + m2.m11; + this.m12 = m1.m12 + m2.m12; + this.m13 = m1.m13 + m2.m13; + this.m20 = m1.m20 + m2.m20; + this.m21 = m1.m21 + m2.m21; + this.m22 = m1.m22 + m2.m22; + this.m23 = m1.m23 + m2.m23; + this.m30 = m1.m30 + m2.m30; + this.m31 = m1.m31 + m2.m31; + this.m32 = m1.m32 + m2.m32; + this.m33 = m1.m33 + m2.m33; + } + + /** 自分に引数の行列を右から掛けたものを格納する */ + public void mul(Matrix4d right) { + double _m00 = m00 * right.m00 + m01 * right.m10 + m02 * right.m20 + m03 * right.m30; + double _m01 = m00 * right.m01 + m01 * right.m11 + m02 * right.m21 + m03 * right.m31; + double _m02 = m00 * right.m02 + m01 * right.m12 + m02 * right.m22 + m03 * right.m32; + double _m03 = m00 * right.m03 + m01 * right.m13 + m02 * right.m23 + m03 * right.m33; + + double _m10 = m10 * right.m00 + m11 * right.m10 + m12 * right.m20 + m13 * right.m30; + double _m11 = m10 * right.m01 + m11 * right.m11 + m12 * right.m21 + m13 * right.m31; + double _m12 = m10 * right.m02 + m11 * right.m12 + m12 * right.m22 + m13 * right.m32; + double _m13 = m10 * right.m03 + m11 * right.m13 + m12 * right.m23 + m13 * right.m33; + + double _m20 = m20 * right.m00 + m21 * right.m10 + m22 * right.m20 + m23 * right.m30; + double _m21 = m20 * right.m01 + m21 * right.m11 + m22 * right.m21 + m23 * right.m31; + double _m22 = m20 * right.m02 + m21 * right.m12 + m22 * right.m22 + m23 * right.m32; + double _m23 = m20 * right.m03 + m21 * right.m13 + m22 * right.m23 + m23 * right.m33; + + double _m30 = m30 * right.m00 + m31 * right.m10 + m32 * right.m20 + m33 * right.m30; + double _m31 = m30 * right.m01 + m31 * right.m11 + m32 * right.m21 + m33 * right.m31; + double _m32 = m30 * right.m02 + m31 * right.m12 + m32 * right.m22 + m33 * right.m32; + double _m33 = m30 * right.m03 + m31 * right.m13 + m32 * right.m23 + m33 * right.m33; + + m00 = _m00; m01 = _m01; m02 = _m02; m03 = _m03; + m10 = _m10; m11 = _m11; m12 = _m12; m13 = _m13; + m20 = _m20; m21 = _m21; m22 = _m22; m23 = _m23; + m30 = _m30; m31 = _m31; m32 = _m32; m33 = _m33; + } + + public void invert() { + double det = determinant(); + if (det == 0) { + return; + } + double b00 = determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33) / det; + double b01 = -determinant3x3(m01,m02,m03,m21,m22,m23,m31,m32,m33) / det; + double b02 = determinant3x3(m01,m02,m03,m11,m12,m13,m31,m32,m33) / det; + double b03 = -determinant3x3(m01,m02,m03,m11,m12,m13,m21,m22,m23) / det; + + double b10 = -determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33) / det; + double b11 = determinant3x3(m00,m02,m03,m20,m22,m23,m30,m32,m33) / det; + double b12 = -determinant3x3(m00,m02,m03,m10,m12,m13,m30,m32,m33) / det; + double b13 = determinant3x3(m00,m02,m03,m10,m12,m13,m20,m22,m23) / det; + + double b20 = determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33) / det; + double b21 = -determinant3x3(m00,m01,m03,m20,m21,m23,m30,m31,m33) / det; + double b22 = determinant3x3(m00,m01,m03,m10,m11,m13,m30,m31,m33) / det; + double b23 = -determinant3x3(m00,m01,m03,m10,m11,m13,m20,m21,m23) / det; + + double b30 = -determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32) / det; + double b31 = determinant3x3(m00,m01,m02,m20,m21,m22,m30,m31,m32) / det; + double b32 = -determinant3x3(m00,m01,m02,m10,m11,m12,m30,m31,m32) / det; + double b33 = determinant3x3(m00,m01,m02,m10,m11,m12,m20,m21,m22) / det; + + m00 = b00; m01 = b01; m02 = b02; m03 = b03; + m10 = b10; m11 = b11; m12 = b12; m13 = b13; + m20 = b20; m21 = b21; m22 = b22; m23 = b23; + m30 = b30; m31 = b31; m32 = b32; m33 = b33; + } + + private static double determinant3x3(double m11, double m12, double m13,double m21,double m22, double m23,double m31,double m32,double m33) { + return m11*m22*m33+m12*m23*m31+m13*m21*m32-m11*m23*m32-m12*m21*m33-m13*m22*m31; + } + + /** 行列式を求める */ + public double determinant() { + return m00 * determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33)- + m01 * determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33)+ + m02 * determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33)- + m03 * determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java new file mode 100644 index 0000000..53ccdb9 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Node.java @@ -0,0 +1,11 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Node { + + public Node() { + super(); + } + + public abstract Node cloneTree(); + +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java new file mode 100644 index 0000000..535ef0d --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/NodeComponent.java @@ -0,0 +1,6 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class NodeComponent { + public abstract NodeComponent cloneNodeComponent(); + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java new file mode 100644 index 0000000..798627d --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3d.java @@ -0,0 +1,68 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Point3d extends Tuple3d { + + // コンストラクタ + public Point3d() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public Point3d(double[] p) { + this.x = p[0]; + this.y = p[1]; + this.z = p[2]; + } + + public Point3d(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3d(Point3d p1) { + this.x = p1.x; + this.y = p1.y; + this.z = p1.z; + } + + public Point3d(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + public void scale(double s) { + this.x = x * s; + this.y = y * s; + this.z = z * s; + } + + public void add(Tuple3d p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + } + + public void sub(Tuple3d p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + } + + public void set(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 引数の点との距離を返す */ + public double distance(Point3d p) { + return Math.sqrt(distanceSquared(p)); + } + + public double distanceSquared(Point3d p) { + return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java new file mode 100644 index 0000000..a3336cf --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Point3f.java @@ -0,0 +1,65 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Point3f { + public float x; + public float y; + public float z; + + // コンストラクタ + public Point3f() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public Point3f(float[] p) { + this.x = p[0]; + this.y = p[1]; + this.z = p[2]; + } + + public Point3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3f(Point3f p1) { + this.x = p1.x; + this.y = p1.y; + this.z = p1.z; + } + + public void scale(float s) { + this.x = x * s; + this.y = y * s; + this.z = z * s; + } + + public void add(Point3f p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + } + + public void sub(Point3f p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + } + + public void set(Point3f v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 引数の点との距離を返す */ + public double distance(Point3f p) { + return Math.sqrt(distanceSquared(p)); + } + + public double distanceSquared(Point3f p) { + return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java new file mode 100644 index 0000000..adbd468 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/PointLight.java @@ -0,0 +1,31 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class PointLight extends Light { + private Point3f position; + private Point3f attenuation; + + public PointLight() { + super(new Color3f(1.0f, 1.0f, 1.0f)); + position = new Point3f(0.0f, 0.0f, 0.0f); + } + + public PointLight(Color3f color, Point3f position, Point3f attenuation){ + super(color); + this.position = position; + this.attenuation = attenuation; + } + + @Override + public Node cloneTree() { + return new PointLight(color, position, attenuation); + } + + public void setPosition(Point3f p){ + this.position = p; + } + + public Point3f getPosition(){ + return position; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java new file mode 100644 index 0000000..ceb8891 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Primitive.java @@ -0,0 +1,15 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Primitive extends Group { + private Appearance ap = null; + + public Appearance getAppearance() { + return ap; + } + + public void setAppearance(Appearance ap) { + this.ap = ap; + } + + public abstract Shape3D getShape(int partid); +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java new file mode 100644 index 0000000..d977c60 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Quat4d.java @@ -0,0 +1,130 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Quat4d implements Cloneable { + private static final double EPS = 1.0e-12; + public double x; + public double y; + public double z; + public double w; + + public Quat4d clone() { + Quat4d q; + try { + q = (Quat4d) super.clone(); + } catch (CloneNotSupportedException ce) { + throw new RuntimeException(); + } + q.set(x, y, z, w); + return q; + } + + // コンストラクタ + public Quat4d() { + x = 0.0; + y = 0.0; + z = 0.0; + w = 0.0; + } + + // コンストラクタ + public Quat4d(double px, double py, double pz, double pw) { + double mag = 1.0/ Math.sqrt(px*px+py*py+pz*pz+pw*pw); + x = px*mag; + y = py*mag; + z = pz*mag; + w = pw*mag; + } + + public Quat4d(Quat4d q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } + + public void set(AxisAngle4d a) { + double mag,amag; + amag = Math.sqrt( a.x*a.x + a.y*a.y + a.z*a.z); + if( amag < EPS ) { + w = 0.0; + x = 0.0; + y = 0.0; + z = 0.0; + } else { + amag = 1.0/amag; + mag = Math.sin(a.angle/2.0); + w = Math.cos(a.angle/2.0); + x = a.x*amag*mag; + y = a.y*amag*mag; + z = a.z*amag*mag; + } + } + + private void set(double x2, double y2, double z2, double w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + public void mul(Quat4d q1) { + double x, y, w; + w = this.w * q1.w - this.x * q1.x - this.y * q1.y - this.z * q1.z; + x = this.w * q1.x + q1.w * this.x + this.y * q1.z - this.z * q1.y; + y = this.w * q1.y + q1.w * this.y - this.x * q1.z + this.z * q1.x; + this.z = this.w * q1.z + q1.w * this.z + this.x * q1.y - this.y * q1.x; + this.w = w; + this.x = x; + this.y = y; + } + + public void interpolate(Quat4d q1, double alpha) { + double dot, s1, s2, om, sinom; + + dot = x * q1.x + y * q1.y + z * q1.z + w * q1.w; + + if (dot < 0) { + // negate quaternion + q1.x = -q1.x; + q1.y = -q1.y; + q1.z = -q1.z; + q1.w = -q1.w; + dot = -dot; + } + + if ((1.0 - dot) > EPS) { + om = Math.acos(dot); + sinom = Math.sin(om); + s1 = Math.sin((1.0 - alpha) * om) / sinom; + s2 = Math.sin(alpha * om) / sinom; + } else { + s1 = 1.0 - alpha; + s2 = alpha; + } + + w = s1 * w + s2 * q1.w; + x = s1 * x + s2 * q1.x; + y = s1 * y + s2 * q1.y; + z = s1 * z + s2 * q1.z; + } + + public void normalize() { + double norm; + + norm = (this.x * this.x + this.y * this.y + this.z * this.z + this.w + * this.w); + + if (norm > 0.0) { + norm = 1.0 / Math.sqrt(norm); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } else { + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + this.w = 0.0; + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java new file mode 100644 index 0000000..aa654de --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Shape3D.java @@ -0,0 +1,33 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Shape3D extends Leaf { + private Geometry geometry; + private Appearance ap; + + public Shape3D(Geometry g, Appearance a) { + this.geometry = g; + this.ap = a; + } + + public Appearance getAppearance() { + return ap; + } + + public void setAppearance(Appearance ap) { + this.ap = ap; + } + + public Geometry getGeometry() { + return geometry; + } + + public void setGeometry(Geometry geometry) { + this.geometry = geometry; + } + + @Override + public Node cloneTree() { + Shape3D shape = new Shape3D(geometry, (Appearance)ap.cloneNodeComponent()); + return shape; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java new file mode 100644 index 0000000..fc06ed7 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Sphere.java @@ -0,0 +1,22 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Sphere extends Primitive { + public static final int BODY = 0; + static final int MID_REZ_DIV = 16; + + private float radius; + int divisions; + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + } + + @Override + public Shape3D getShape(int partid) { + return null; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java new file mode 100644 index 0000000..3a39355 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TexCoordGeneration.java @@ -0,0 +1,42 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TexCoordGeneration extends NodeComponent { + public static final int OBJECT_LINEAR = 0; + public static final int EYE_LINEAR = 1; + public static final int SPHERE_MAP = 2; + public static final int NORMAL_MAP = 3; + public static final int REFLECTION_MAP = 4; + public static final int TEXTURE_COORDINATE_2 = 0; + public static final int TEXTURE_COORDINATE_3 = 1; + public static final int TEXTURE_COORDINATE_4 = 2; + + private int genMode; + private int format; + + public TexCoordGeneration(int genMode, int format) { + this.genMode = genMode; + this.format = format; + } + + public int getGenMode() { + return genMode; + } + + public void setGenMode(int genMode) { + this.genMode = genMode; + } + + public int getFormat() { + return format; + } + + public void setFormat(int format) { + this.format = format; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TexCoordGeneration(genMode, format); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java new file mode 100644 index 0000000..e8217f0 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture.java @@ -0,0 +1,36 @@ +package com.google.ar.core.examples.java.common.java3d; + +public abstract class Texture extends NodeComponent { + public static final int BASE_LEVEL = 1; + public static final int RGB = 5; + public static final int RGBA = 6; + + protected ImageComponent[] imageComponents = null; + protected int mipmapMode; + protected int format; + protected int width; + protected int hight; + + public Texture(int mipmapMode, int format, int width, int hight) { + this.mipmapMode = mipmapMode; + this.format = format; + this.width = width; + this.hight = hight; + } + + public void setImage(int n, ImageComponent image) { + imageComponents[n] = image; + } + + public ImageComponent getImage(int n) { + return imageComponents[n]; + } + + public void setImages(ImageComponent[] images) { + imageComponents = images; + } + + public ImageComponent[] getImages() { + return imageComponents; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java new file mode 100644 index 0000000..62627e8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Texture2D.java @@ -0,0 +1,16 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Texture2D extends Texture { + + public Texture2D(int mipmapMode, int format, int width, int hight) { + super(mipmapMode, format, width, hight); + imageComponents = new ImageComponent[1]; + } + + @Override + public NodeComponent cloneNodeComponent() { + Texture2D tex2D = new Texture2D(mipmapMode, format, width, hight); + tex2D.imageComponents = imageComponents.clone(); + return tex2D; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java new file mode 100644 index 0000000..ba7679e --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureAttributes.java @@ -0,0 +1,70 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TextureAttributes extends NodeComponent { + public static final int FASTEST = 0; + public static final int NICEST = 1; + public static final int MODULATE = 2; + public static final int DECAL = 3; + public static final int BLEND = 4; + public static final int REPLACE = 5; +// public static final int COMBINE = 6; +// public static final int COMBINE_REPLACE = 0; +// public static final int COMBINE_MODULATE = 1; +// public static final int COMBINE_ADD = 2; +// public static final int COMBINE_ADD_SIGNED = 3; +// public static final int COMBINE_SUBTRACT = 4; +// public static final int COMBINE_INTERPOLATE = 5; +// public static final int COMBINE_DOT3 = 6; +// public static final int COMBINE_OBJECT_COLOR = 0; +// public static final int COMBINE_TEXTURE_COLOR = 1; +// public static final int COMBINE_CONSTANT_COLOR = 2; +// public static final int COMBINE_PREVIOUS_TEXTURE_UNIT_STATE = 3; +// public static final int COMBINE_SRC_COLOR = 0; +// public static final int COMBINE_ONE_MINUS_SRC_COLOR = 1; +// public static final int COMBINE_SRC_ALPHA = 2; +// public static final int COMBINE_ONE_MINUS_SRC_ALPHA = 3; + + private int textureMode; + private Transform3D transform; + private Color4f textureBlendColor; + private int perspCorrectionMode; + + public TextureAttributes() { + textureMode = TextureAttributes.REPLACE; + textureBlendColor = new Color4f(); + perspCorrectionMode = TextureAttributes.NICEST; + } + + public TextureAttributes(int textureMode, Transform3D transform, Color4f textureBlendColor, int perspCorrectionMode) { + this.textureMode = textureMode; + this.transform = transform; + this.textureBlendColor = textureBlendColor; + this.perspCorrectionMode = perspCorrectionMode; + } + + public int getTextureMode() { + return textureMode; + } + + public void setTextureMode(int textureMode) { + this.textureMode = textureMode; + } + + public void setTextureTransform(Transform3D transform) { + this.transform = transform; + } + + public int getPerspectiveCorrectionMode() { + return perspCorrectionMode; + } + + public void setPerspectiveCorrectionMode(int perspCorrectionMode) { + this.perspCorrectionMode = perspCorrectionMode; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TextureAttributes(textureMode, new Transform3D(transform), textureBlendColor.clone(), perspCorrectionMode); + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java new file mode 100644 index 0000000..3730dba --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureCubeMap.java @@ -0,0 +1,29 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TextureCubeMap extends Texture { + public static final int POSITIVE_X = 0; + public static final int NEGATIVE_X = 1; + public static final int POSITIVE_Y = 2; + public static final int NEGATIVE_Y = 3; + public static final int POSITIVE_Z = 4; + public static final int NEGATIVE_Z = 5; + + public TextureCubeMap(int mipmapMode, int format, int width) { + super(mipmapMode, format, width, 0); + imageComponents = new ImageComponent[6]; + } + + + public void setImage(int level, int face, ImageComponent2D image) { + imageComponents[face] = image; + width = image.getBitmap().getWidth(); + } + + @Override + public NodeComponent cloneNodeComponent() { + TextureCubeMap texCubeMap = new TextureCubeMap(mipmapMode, format, width); + texCubeMap.imageComponents = imageComponents.clone(); + return texCubeMap; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java new file mode 100644 index 0000000..283e5a8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureLoader.java @@ -0,0 +1,43 @@ +package com.google.ar.core.examples.java.common.java3d; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.InputStream; + +public class TextureLoader { + + public static final int BY_REFERENCE = 2; + public static final int Y_UP = 4; + private InputStream in = null; + private Resources res = null; + private int id = 0; + private Bitmap bitmap; + + public TextureLoader(InputStream in, int flags) { + this.in = in; + } + + public TextureLoader(Resources res, int id, int flags) { + this.res = res; + this.id = id; + } + + public ImageComponent2D getImage() { + if (in != null) { + bitmap = BitmapFactory.decodeStream(in); + } else if (res != null) { + bitmap = BitmapFactory.decodeResource(res, id); + } + return new ImageComponent2D(ImageComponent2D.FORMAT_RGB, bitmap); + } + + public Texture getTexture() { + ImageComponent2D ic2 = getImage(); + Texture tex = new Texture2D(Texture.BASE_LEVEL, Texture.RGB, bitmap.getWidth(), bitmap.getHeight()); + tex.setImage(0, ic2); + return tex; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java new file mode 100644 index 0000000..4ffa3c8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TextureUnitState.java @@ -0,0 +1,48 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TextureUnitState extends NodeComponent { + private Texture texture; + private TextureAttributes textureAttribute; + private TexCoordGeneration texCoordGeneration; + + public TextureUnitState() { + this(null, null ,null); + } + + public TextureUnitState(Texture texture, TextureAttributes textureAttribute, TexCoordGeneration texCoordGeneration) { + this.texture = texture; + this.textureAttribute = textureAttribute; + this.texCoordGeneration= texCoordGeneration; + } + + public Texture getTexture() { + return texture; + } + + public void setTexture(Texture texture) { + this.texture = texture; + } + + public TextureAttributes getTextureAttributes() { + return textureAttribute; + } + + public void setTextureAttributes(TextureAttributes textureAttribute) { + this.textureAttribute = textureAttribute; + } + + public TexCoordGeneration getTexCoordGeneration() { + return texCoordGeneration; + } + + public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) { + this.texCoordGeneration = texCoordGeneration; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TextureUnitState((Texture)texture.cloneNodeComponent(), + (TextureAttributes)textureAttribute.cloneNodeComponent(), + (TexCoordGeneration)texCoordGeneration.cloneNodeComponent()); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java new file mode 100644 index 0000000..efeef90 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Transform3D.java @@ -0,0 +1,425 @@ +package com.google.ar.core.examples.java.common.java3d; + + +public class Transform3D { + double[][] mat; + double[] rot; + double[] scales = new double[] {1.0,1.0,1.0}; + + static final double EPSILON_ABSOLUTE = 1.0e-5; + + public Transform3D() { + mat = new double[][]{{1.0,0.0,0.0,0.0},{0.0,1.0,0.0,0.0},{0.0,0.0,1.0,0.0},{0.0,0.0,0.0,1.0}}; + } + + public Transform3D(Matrix4d m1) { + mat = new double[][]{{m1.m00,m1.m01,m1.m02,m1.m03},{m1.m10,m1.m11,m1.m12,m1.m13},{m1.m20,m1.m21,m1.m22,m1.m23},{m1.m30,m1.m31,m1.m32,m1.m33}}; + } + + public Transform3D(Transform3D transform) { + mat = new double[4][4]; + set(transform); + } + + public void set(Transform3D transform) { + for (int i = 0;i < 4; i++) { + for (int j = 0;j < 4; j++) { + mat[i][j] = transform.mat[i][j]; + } + } + scales[0] = transform.scales[0]; + scales[1] = transform.scales[1]; + scales[2] = transform.scales[2]; + } + + /** + * this = t1 * t2 + * + * @param t1 + * @param t2 + */ + public void mul(Transform3D t1, Transform3D t2) { + for (int i = 0; i < 4; i++) { + double[] row = row(i, t1.mat); + for (int j = 0; j < 4; j++) { + mat[i][j] = 0.0; + double[] col = col(j, t2.mat); + for (int k = 0; k < 4; k++) { + mat[i][j] += row[k] * col[k]; + } + } + } + } + + /** + * this = this * t1 + * + * @param t1 + */ + public void mul(Transform3D t1) { + this.mul(new Transform3D(this), t1); + } + + /** 4*4行列の行を返す */ + private double[] row(int row, double[][] mat) { + return new double[] {mat[row][0], mat[row][1], mat[row][2], mat[row][3]}; + } + + /** 4*4行列の列を返す */ + private double[] col(int col, double[][] mat) { + return new double[] {mat[0][col], mat[1][col], mat[2][col], mat[3][col]}; + } + + public void set(Vector3d v) { + mat[0][3] = v.x; + mat[1][3] = v.y; + mat[2][3] = v.z; + } + + /** 4*4の行列を16の1次元配列で返す */ + public float[] getMatrix() { + float[] result = new float[16]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + result[j + 4 * i] = (float)(mat[j][i]); + } + } + return result; + } + + /** 対角行列をスカラ倍する */ + public void setScale(double s) { + for (int i = 0; i < 3; i++) { + mat[i][i] = s; + scales[i] = s; + } + } + + /** 対角行列をvectorの成分倍する */ + public void setScale(Vector3d vector3d) { + scales[0] = mat[0][0] = vector3d.x; + scales[1] = mat[1][1] = vector3d.y; + scales[2] = mat[2][2] = vector3d.z; + } + + /** 任意の単位ベクトルまわりに回転する */ + public void setRotation(AxisAngle4d t) { + double vx = t.x; + double vy = t.y; + double vz = t.z; + double sin = Math.sin(t.angle); + double cos = Math.cos(t.angle); + + mat[0][0] = (Math.pow(vx, 2.0)) * (1 - cos) + cos; + mat[0][1] = ((vx * vy) * (1 - cos)) - (vz * sin); + mat[0][2] = ((vz * vx) * (1 - cos)) + (vy * sin); + mat[1][0] = ((vx * vy) * (1 - cos)) + (vz * sin); + mat[1][1] = (Math.pow(vy, 2.0)) * (1 - cos) + cos; + mat[1][2] = ((vy * vz) * (1 - cos)) - (vx * sin); + mat[2][0] = ((vz * vx) * (1 - cos)) - (vy * sin); + mat[2][1] = ((vy * vz) * (1 - cos)) + (vx * sin); + mat[2][2] = (Math.pow(vz, 2.0)) * (1 - cos) + cos; + } + + /** 任意の単位ベクトルまわりに回転する */ + public void setRotation(Quat4d q) { +// double vx = q.x; +// double vy = q.y; +// double vz = q.z; +// double sin = Math.sin(q.w); +// double cos = Math.cos(q.w); +// double cosm = 1 - cos; +// +// mat[0][0] = (vx * vx) * cosm + cos; +// mat[0][1] = ((vx * vy) * cosm) - (vz * sin); +// mat[0][2] = ((vz * vx) * cosm) + (vy * sin); +// mat[1][0] = ((vx * vy) * cosm) + (vz * sin); +// mat[1][1] = (vy * vy) * cosm + cos; +// mat[1][2] = ((vy * vz) * cosm) - (vx * sin); +// mat[2][0] = ((vz * vx) * cosm) - (vy * sin); +// mat[2][1] = ((vy * vz) * cosm) + (vx * sin); +// mat[2][2] = (vz * vz) * cosm + cos; + + mat[0][0] = (1.0 - 2.0 * q.y * q.y - 2.0 * q.z * q.z) * scales[0]; + mat[1][0] = (2.0 * (q.x * q.y + q.w * q.z)) * scales[0]; + mat[2][0] = (2.0 * (q.x * q.z - q.w * q.y)) * scales[0]; + mat[0][1] = (2.0 * (q.x * q.y - q.w * q.z)) * scales[1]; + mat[1][1] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.z * q.z) * scales[1]; + mat[2][1] = (2.0 * (q.y * q.z + q.w * q.x)) * scales[1]; + mat[0][2] = (2.0 * (q.x * q.z + q.w * q.y)) * scales[2]; + mat[1][2] = (2.0 * (q.y * q.z - q.w * q.x)) * scales[2]; + mat[2][2] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.y * q.y) * scales[2]; + } + + public void rotX(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = 1.0; + mat[0][1] = 0.0; + mat[0][2] = 0.0; + mat[0][3] = 0.0; + + mat[1][0] = 0.0; + mat[1][1] = cosAngle; + mat[1][2] = -sinAngle; + mat[1][3] = 0.0; + + mat[2][0] = 0.0; + mat[2][1] = sinAngle; + mat[2][2] = cosAngle; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + public void rotY(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = cosAngle; + mat[0][1] = 0.0; + mat[0][2] = sinAngle; + mat[0][3] = 0.0; + + mat[1][0] = 0.0; + mat[1][1] = 1.0; + mat[1][2] = 0.0; + mat[1][3] = 0.0; + + mat[2][0] = -sinAngle; + mat[2][1] = 0.0; + mat[2][2] = cosAngle; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + public void rotZ(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = cosAngle; + mat[0][1] = -sinAngle; + mat[0][2] = 0.0; + mat[0][3] = 0.0; + + mat[1][0] = sinAngle; + mat[1][1] = cosAngle; + mat[1][2] = 0.0; + mat[1][3] = 0.0; + + mat[2][0] = 0.0; + mat[2][1] = 0.0; + mat[2][2] = 1.0; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + /** 引数の成分を格納する */ + public void get(Matrix4d mat4d) { + mat4d.m00 = mat[0][0]; + mat4d.m01 = mat[0][1]; + mat4d.m02 = mat[0][2]; + mat4d.m03 = mat[0][3]; + mat4d.m10 = mat[1][0]; + mat4d.m11 = mat[1][1]; + mat4d.m12 = mat[1][2]; + mat4d.m13 = mat[1][3]; + mat4d.m20 = mat[2][0]; + mat4d.m21 = mat[2][1]; + mat4d.m22 = mat[2][2]; + mat4d.m23 = mat[2][3]; + mat4d.m30 = mat[3][0]; + mat4d.m31 = mat[3][1]; + mat4d.m32 = mat[3][2]; + mat4d.m33 = mat[3][3]; + } + + + public void set(double[] matrix) { + mat[0][0] = matrix[0]; + mat[0][1] = matrix[1]; + mat[0][2] = matrix[2]; + mat[0][3] = matrix[3]; + mat[1][0] = matrix[4]; + mat[1][1] = matrix[5]; + mat[1][2] = matrix[6]; + mat[1][3] = matrix[7]; + mat[2][0] = matrix[8]; + mat[2][1] = matrix[9]; + mat[2][2] = matrix[10]; + mat[2][3] = matrix[11]; + mat[3][0] = matrix[12]; + mat[3][1] = matrix[13]; + mat[3][2] = matrix[14]; + mat[3][3] = matrix[15]; + scales[0] = 1.0; + scales[1] = 1.0; + scales[2] = 1.0; + } + + public void set(AxisAngle4d a1) { + double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z); + if (almostZero(mag)) { + setIdentity(); + } else { + mag = 1.0/mag; + double ax = a1.x*mag; + double ay = a1.y*mag; + double az = a1.z*mag; + double sinTheta = Math.sin((double)a1.angle); + double cosTheta = Math.cos((double)a1.angle); + double t = 1.0 - cosTheta; + double xz = ax * az; + double xy = ax * ay; + double yz = ay * az; + mat[0][0] = t * ax * ax + cosTheta; + mat[0][1] = t * xy - sinTheta * az; + mat[0][2] = t * xz + sinTheta * ay; + mat[0][3] = 0.0; + mat[1][0] = t * xy + sinTheta * az; + mat[1][1] = t * ay * ay + cosTheta; + mat[1][2] = t * yz - sinTheta * ax; + mat[1][3] = 0.0; + mat[2][0] = t * xz - sinTheta * ay; + mat[2][1] = t * yz + sinTheta * ax; + mat[2][2] = t * az * az + cosTheta; + mat[2][3] = 0.0; + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + } + + public void set(Quat4d q1) { + mat[0][0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z); + mat[1][0] = (2.0f*(q1.x*q1.y + q1.w*q1.z)); + mat[2][0] = (2.0f*(q1.x*q1.z - q1.w*q1.y)); + + mat[0][1] = (2.0f*(q1.x*q1.y - q1.w*q1.z)); + mat[1][1] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z); + mat[2][1] = (2.0f*(q1.y*q1.z + q1.w*q1.x)); + + mat[0][2] = (2.0f*(q1.x*q1.z + q1.w*q1.y)); + mat[1][2] = (2.0f*(q1.y*q1.z - q1.w*q1.x)); + mat[2][2] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y); + + mat[0][3] = 0.0; + mat[1][3] = 0.0; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + + // Issue 253: set all dirty bits if input is infinity or NaN +// if (isInfOrNaN(q1)) { +// dirtyBits = ALL_DIRTY; +// return; +// } + +// dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT; +// type = RIGID | CONGRUENT | AFFINE | ORTHO; + } + + /** Vector3dをTransform3dで変換する */ + public void transform(Vector3d v) { + double vx = mat[0][0]*v.x + mat[0][1]*v.y + mat[0][2]*v.z;// + mat[0][3] * 1.0; + double vy = mat[1][0]*v.x + mat[1][1]*v.y + mat[1][2]*v.z;// + mat[1][3] * 1.0; + v.z = mat[2][0]*v.x + mat[2][1]*v.y + mat[2][2]*v.z;// + mat[2][3] * 1.0; + v.x = vx; + v.y = vy; + } + + private static final boolean almostZero(double a) { + return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); + } + + public final void setIdentity() { + mat[0][0] = 1.0; mat[0][1] = 0.0; mat[0][2] = 0.0; mat[0][3] = 0.0; + mat[1][0] = 0.0; mat[1][1] = 1.0; mat[1][2] = 0.0; mat[1][3] = 0.0; + mat[2][0] = 0.0; mat[2][1] = 0.0; mat[2][2] = 1.0; mat[2][3] = 0.0; + mat[3][0] = 0.0; mat[3][1] = 0.0; mat[3][2] = 0.0; mat[3][3] = 1.0; + } + + public void invert() { + Matrix4d m = new Matrix4d(); + m.m00 = mat[0][0]; m.m01 = mat[0][1]; m.m02 = mat[0][2]; m.m03 = mat[0][3]; + m.m10 = mat[1][0]; m.m11 = mat[1][1]; m.m12 = mat[1][2]; m.m13 = mat[1][3]; + m.m20 = mat[2][0]; m.m21 = mat[2][1]; m.m22 = mat[2][2]; m.m23 = mat[2][3]; + m.m30 = mat[3][0]; m.m31 = mat[3][1]; m.m32 = mat[3][2]; m.m33 = mat[3][3]; + m.invert(); + mat = new double[][]{{m.m00,m.m01,m.m02,m.m03},{m.m10,m.m11,m.m12,m.m13},{m.m20,m.m21,m.m22,m.m23},{m.m30,m.m31,m.m32,m.m33}}; + } + + public void transpose() { + for (int i = 0; i < 3; i++) { + for (int j = 1 + i; j < 4; j++) { + double m = mat[j][i]; + mat[j][i] = mat[i][j]; + mat[i][j] = m; + } + } + } + + public void transform(Point3d point) { + Point3d p = new Point3d(); + p.x = mat[0][0] * point.x + mat[0][1] * point.y + mat[0][2] * point.z; + p.y = mat[1][0] * point.x + mat[1][1] * point.y + mat[1][2] * point.z; + p.z = mat[2][0] * point.x + mat[2][1] * point.y + mat[2][2] * point.z; + point.x = p.x; + point.y = p.y; + point.z = p.z; + } + + /** + * 平面に対してしか使ってはいけない + * @param plane + */ + public void transform(Vector4d plane) { +// double x = mat[0][0] * plane.x + mat[0][1] * plane.y + mat[0][2] * plane.z; +// double y = mat[1][0] * plane.x + mat[1][1] * plane.y + mat[1][2] * plane.z; +// double z = mat[2][0] * plane.x + mat[2][1] * plane.y + mat[2][2] * plane.z; +// +// double vx = x * plane.w - mat[0][3]; +// double vy = y * plane.w - mat[1][3]; +// double vz = z * plane.w - mat[2][3]; +// +// plane.x = x; +// plane.y = y; +// plane.z = z; +// plane.w = x * vx + y * vy + z * vz; +// + double x = (mat[0][0]*plane.x + mat[0][1]*plane.y + + mat[0][2]*plane.z + mat[0][3]*plane.w); + double y = (mat[1][0]*plane.x + mat[1][1]*plane.y + + mat[1][2]*plane.z + mat[1][3]*plane.w); + double z = (mat[2][0]*plane.x + mat[2][1]*plane.y + + mat[2][2]*plane.z + mat[2][3]*plane.w); + plane.w = (mat[3][0]*plane.x + mat[3][1]*plane.y + + mat[3][2]*plane.z + mat[3][3]*plane.w); + plane.x = x; + plane.y = y; + plane.z = z; + } + + public void setTranslation(Vector3d v) { + mat[3][0] = v.x; + mat[3][1] = v.y; + mat[3][2] = v.z; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java new file mode 100644 index 0000000..1b8a692 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TransformGroup.java @@ -0,0 +1,27 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TransformGroup extends Group { + + private Transform3D transform; + + public TransformGroup() { + this(new Transform3D()); + } + + public TransformGroup(Transform3D transform) { + this.transform = transform; + } + + public void setTransform(Transform3D transform) { + this.transform = transform; + } + + public void getTransform(Transform3D transform) { + transform.set(this.transform); + } + + public Node cloneTree() { + TransformGroup newInstance = new TransformGroup(new Transform3D(transform)); + return newInstance; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java new file mode 100644 index 0000000..905def7 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleArray.java @@ -0,0 +1,18 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TriangleArray extends GeometryArray { + + public TriangleArray(int vertexCount, int vertexFormat) { + super(vertexCount, vertexFormat); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleArray newOne = new TriangleArray(vertexCount, vertexFormat); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java new file mode 100644 index 0000000..9cd80b0 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleFanArray.java @@ -0,0 +1,18 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TriangleFanArray extends GeometryStripArray { + + public TriangleFanArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleFanArray newOne = new TriangleFanArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java new file mode 100644 index 0000000..8a55630 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/TriangleStripArray.java @@ -0,0 +1,18 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class TriangleStripArray extends GeometryStripArray { + + public TriangleStripArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleStripArray newOne = new TriangleStripArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java new file mode 100644 index 0000000..d128dd8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Tuple3d.java @@ -0,0 +1,38 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Tuple3d { + + public double x; + public double y; + public double z; + + public void set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java new file mode 100644 index 0000000..fe6fdbc --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector2f.java @@ -0,0 +1,104 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Vector2f { + public float x; + public float y; + + // コンストラクタ + public Vector2f() { + x = 0.0f; + y = 0.0f; + } + + // コンストラクタ + public Vector2f(float px, float py) { + x = px; + y = py; + } + + public Vector2f(Vector2f v) { + x = v.x; + y = v.y; + } + + public Vector2f clone() { + return new Vector2f(this.x, this.y); + } + + public double dot(Vector2f a) { + return x*a.x + y*a.y; + } + + public float length() { + return (float) Math.sqrt(x*x + y*y); + } + + public void scale(float s) {; + x = x*s; + y = y*s; + } + + public void normalize() { + float l = length(); + x = x / l; + y = y / l; + } + + /** v1とv2の和を格納する */ + public void add(Vector2f v1, Vector2f v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + } + + /** v1との和を格納する */ + public void add(Vector2f v1) { + this.x += v1.x; + this.y += v1.y; + } + + /** v1とv2の差を格納する */ + public void sub(Vector2f v1, Vector2f v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + } + + /** v1との差を格納する */ + public void sub(Vector2f v1) { + this.x -= v1.x; + this.y -= v1.y; + } + + /** Vector3dをセットする */ + public void set(Vector2f v) { + this.x = v.x; + this.y = v.y; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + } + + /** this = d * axis + p */ + public void scaleAdd(float d, Vector2f axis, Vector2f p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java new file mode 100644 index 0000000..35a31b7 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3d.java @@ -0,0 +1,134 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Vector3d extends Tuple3d { + + // コンストラクタ + public Vector3d() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public Vector3d(double px, double py, double pz) { + x = px; + y = py; + z = pz; + } + + public Vector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3d(Tuple3d p) { + x = p.x; + y = p.y; + z = p.z; + } + + public Vector3d(double[] coordinate) { + x = coordinate[0]; + y = coordinate[1]; + z = coordinate[2]; + } + + public Vector3d clone() { + return new Vector3d(this.x, this.y, this.z); + } + + public double dot(Vector3d a) { + return x*a.x + y*a.y + z*a.z; + } + + public void cross(Vector3d a, Vector3d b) { + double x = a.y*b.z - a.z*b.y; + double y = a.z*b.x - a.x*b.z; + double z = a.x*b.y - a.y*b.x; + this.x = x; + this.y = y; + this.z = z; + } + + public double length() { + return Math.sqrt(x*x+y*y+z*z); + } + + public void scale(double s) { + x = x*s; + y = y*s; + z = z*s; + } + + public void normalize() { + double l = length(); + x = x / l; + y = y / l; + z = z / l; + } + + /** v1とv2の和を格納する */ + public void add(Tuple3d v1, Tuple3d v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + } + + /** v1との和を格納する */ + public void add(Tuple3d v1) { + this.x += v1.x; + this.y += v1.y; + this.z += v1.z; + } + + /** v1とv2の差を格納する */ + public void sub(Tuple3d v1, Tuple3d v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + } + + /** v1との差を格納する */ + public void sub(Tuple3d v1) { + this.x -= v1.x; + this.y -= v1.y; + this.z -= v1.z; + } + + /** Vector3dをセットする */ + public void set(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + this.z = -z; + } + + /** this = d * axis + p */ + public void scaleAdd(double d, Vector3d axis, Vector3d p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + this.z = d * axis.z + p.z; + } + + public void negate(Vector3d v) { + this.x = -v.x; + this.y = -v.y; + this.z = -v.z; + } + + public double angle(Vector3d v) { + double d = dot(v) / (length() * v.length()); + if (d < -1D) + d = -1D; + if (d > 1.0D) + d = 1.0D; + return Math.acos(d); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java new file mode 100644 index 0000000..1c2eb65 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector3f.java @@ -0,0 +1,134 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Vector3f { + public float x; + public float y; + public float z; + + // コンストラクタ + public Vector3f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + // コンストラクタ + public Vector3f(float px, float py, float pz) { + x = px; + y = py; + z = pz; + } + + public Vector3f(Vector3f v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3f clone() { + return new Vector3f(this.x, this.y, this.z); + } + + public double dot(Vector3f a) { + return x*a.x + y*a.y + z*a.z; + } + + public void cross(Vector3f a, Vector3f b) { + float x = a.y*b.z - a.z*b.y; + float y = a.z*b.x - a.x*b.z; + float z = a.x*b.y - a.y*b.x; + this.x = x; + this.y = y; + this.z = z; + } + + public float length() { + return (float) Math.sqrt(x*x + y*y + z*z); + } + + public void scale(float s) {; + x = x*s; + y = y*s; + z = z*s; + } + + public void normalize() { + float l = length(); + x = x / l; + y = y / l; + z = z / l; + } + + /** v1とv2の和を格納する */ + public void add(Vector3f v1, Vector3f v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + } + + /** v1との和を格納する */ + public void add(Vector3f v1) { + this.x += v1.x; + this.y += v1.y; + this.z += v1.z; + } + + /** v1とv2の差を格納する */ + public void sub(Vector3f v1, Vector3f v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + } + + /** v1との差を格納する */ + public void sub(Vector3f v1) { + this.x -= v1.x; + this.y -= v1.y; + this.z -= v1.z; + } + + /** Vector3dをセットする */ + public void set(Vector3f v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + this.z = -z; + } + + /** this = d * axis + p */ + public void scaleAdd(float d, Vector3f axis, Vector3f p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + this.z = d * axis.z + p.z; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public void setZ(float z) { + this.z = z; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java new file mode 100644 index 0000000..f639da6 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4d.java @@ -0,0 +1,184 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Vector4d { + public double x; + public double y; + public double z; + public double w; + + // コンストラクタ + public Vector4d() { + x = 0.0; + y = 0.0; + z = 0.0; + w = 0.0; + } + + // コンストラクタ + public Vector4d(double px, double py, double pz, double pw) { + x = px; + y = py; + z = pz; + w = pw; + } + + public Vector4d(Vector4d v) { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + } + + public void set(double x2, double y2, double z2, double w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + private void set(double[] t) { + x = t[0]; + y = t[1]; + z = t[2]; + w = t[3]; + } + + public Vector4d clone() { + return new Vector4d(x, y, z, w); + } + + /** 絶対値を返す */ + final void absolute() { + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + w = Math.abs(w); + } + + final double angle(Vector4d v1) { + double vDot = this.dot(v1) / (this.length() * v1.length()); + if (vDot < -1.0) + vDot = -1.0; + if (vDot > 1.0) + vDot = 1.0; + return ((double) (Math.acos(vDot))); + } + + public final double dot(Vector4d v1) { + return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w); + } + + public final double length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + + this.w * this.w); + } + + final void clamp(double min, double max) { + if (x > max) { + x = max; + } else if (x < min) { + x = min; + } + + if (y > max) { + y = max; + } else if (y < min) { + y = min; + } + + if (z > max) { + z = max; + } else if (z < min) { + z = min; + } + + if (w > max) { + w = max; + } else if (w < min) { + w = min; + } + } + + final void clampMax(double max) { + if (x > max) + x = max; + if (y > max) + y = max; + if (z > max) + z = max; + if (w > max) + w = max; + } + + final void clampMin(double min) { + if (x < min) + x = min; + if (y < min) + y = min; + if (z < min) + z = min; + if (w < min) + w = min; + } + + final void normalize() { + double norm; + + norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z + * this.z + this.w * this.w); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } + + final void normalize(Vector4d v1) { + double norm; + + norm = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w + * v1.w); + this.x = v1.x * norm; + this.y = v1.y * norm; + this.z = v1.z * norm; + this.w = v1.w * norm; + } + + final void get(double[] t) { + t[0] = this.x; + t[1] = this.y; + t[2] = this.z; + t[3] = this.w; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public double getW() { + return w; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setW(double w) { + this.w = w; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java new file mode 100644 index 0000000..a00fbad --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/Vector4f.java @@ -0,0 +1,183 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class Vector4f { + public float x; + public float y; + public float z; + public float w; + + // コンストラクタ + public Vector4f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 0.0f; + } + + // コンストラクタ + public Vector4f(float px, float py, float pz, float pw) { + x = px; + y = py; + z = pz; + w = pw; + } + + public Vector4f(Vector4f v) { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + } + + public void set(float x2, float y2, float z2, float w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + private void set(float[] t) { + x = t[0]; + y = t[1]; + z = t[2]; + w = t[3]; + } + + public Vector4f clone() { + return new Vector4f(x, y, z, w); + } + + /** 絶対値を返す */ + final void absolute() { + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + w = Math.abs(w); + } + + final double angle(Vector4f v1) { + double vDot = this.dot(v1) / (this.length() * v1.length()); + if (vDot < -1.0) + vDot = -1.0; + if (vDot > 1.0) + vDot = 1.0; + return ((double) (Math.acos(vDot))); + } + + public final double dot(Vector4f v1) { + return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w); + } + + public final double length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + + this.w * this.w); + } + + final void clamp(float min, float max) { + if (x > max) { + x = max; + } else if (x < min) { + x = min; + } + + if (y > max) { + y = max; + } else if (y < min) { + y = min; + } + + if (z > max) { + z = max; + } else if (z < min) { + z = min; + } + + if (w > max) { + w = max; + } else if (w < min) { + w = min; + } + } + + final void clampMax(float max) { + if (x > max) + x = max; + if (y > max) + y = max; + if (z > max) + z = max; + if (w > max) + w = max; + } + + final void clampMin(float min) { + if (x < min) + x = min; + if (y < min) + y = min; + if (z < min) + z = min; + if (w < min) + w = min; + } + + final void normalize() { + double norm; + + norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z + * this.z + this.w * this.w); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } + + final void normalize(Vector4f v1) { + float norm; + + norm = 1.0f / (float) Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w * v1.w); + this.x = v1.x * norm; + this.y = v1.y * norm; + this.z = v1.z * norm; + this.w = v1.w * norm; + } + + final void get(float[] t) { + t[0] = this.x; + t[1] = this.y; + t[2] = this.z; + t[3] = this.w; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } + + public float getW() { + return w; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public void setZ(float z) { + this.z = z; + } + + public void setW(float w) { + this.w = w; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java new file mode 100644 index 0000000..a59a8c8 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/java3d/View.java @@ -0,0 +1,8 @@ +package com.google.ar.core.examples.java.common.java3d; + +public class View { + + public View() { + + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java new file mode 100644 index 0000000..d0070af --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/BackgroundRenderer.java @@ -0,0 +1,237 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.rendering; + +import android.content.Context; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.support.annotation.NonNull; +import com.google.ar.core.Coordinates2d; +import com.google.ar.core.Frame; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * This class renders the AR background from camera feed. It creates and hosts the texture given to + * ARCore to be filled with the camera image. + */ +public class BackgroundRenderer { + private static final String TAG = BackgroundRenderer.class.getSimpleName(); + + // Shader names. + private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert"; + private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag"; + + private static final int COORDS_PER_VERTEX = 2; + private static final int TEXCOORDS_PER_VERTEX = 2; + private static final int FLOAT_SIZE = 4; + + private FloatBuffer quadCoords; + private FloatBuffer quadTexCoords; + + private int quadProgram; + + private int quadPositionParam; + private int quadTexCoordParam; + private int textureId = -1; + + public int getTextureId() { + return textureId; + } + + /** + * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on + * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, + * EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) throws IOException { + // Generate the background texture. + int[] textures = new int[1]; + GLES20.glGenTextures(1, textures, 0); + textureId = textures[0]; + int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; + GLES20.glBindTexture(textureTarget, textureId); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + + int numVertices = 4; + if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) { + throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer."); + } + + ByteBuffer bbCoords = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE); + bbCoords.order(ByteOrder.nativeOrder()); + quadCoords = bbCoords.asFloatBuffer(); + quadCoords.put(QUAD_COORDS); + quadCoords.position(0); + + ByteBuffer bbTexCoordsTransformed = + ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); + bbTexCoordsTransformed.order(ByteOrder.nativeOrder()); + quadTexCoords = bbTexCoordsTransformed.asFloatBuffer(); + + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); + int fragmentShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); + + quadProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(quadProgram, vertexShader); + GLES20.glAttachShader(quadProgram, fragmentShader); + GLES20.glLinkProgram(quadProgram); + GLES20.glUseProgram(quadProgram); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position"); + quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + } + + /** + * Draws the AR background image. The image will be drawn such that virtual content rendered with + * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and + * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will + * accurately follow static physical objects. This must be called before drawing virtual + * content. + * + * @param frame The current {@code Frame} as returned by {@link Session#update()}. + */ + public void draw(@NonNull Frame frame) { + // If display rotation changed (also includes view size change), we need to re-query the uv + // coordinates for the screen rect, as they may have changed as well. + if (frame.hasDisplayGeometryChanged()) { + frame.transformCoordinates2d( + Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES, + quadCoords, + Coordinates2d.TEXTURE_NORMALIZED, + quadTexCoords); + } + + if (frame.getTimestamp() == 0) { + // Suppress rendering if the camera did not produce the first frame yet. This is to avoid + // drawing possible leftover data from previous sessions if the texture is reused. + return; + } + + draw(); + } + + /** + * Draws the camera image using the currently configured {@link BackgroundRenderer#quadTexCoords} + * image texture coordinates. + * + *

The image will be center cropped if the camera sensor aspect ratio does not match the screen + * aspect ratio, which matches the cropping behavior of {@link + * Frame#transformCoordinates2d(Coordinates2d, float[], Coordinates2d, float[])}. + */ + public void draw( + int imageWidth, int imageHeight, float screenAspectRatio, int cameraToDisplayRotation) { + // Crop the camera image to fit the screen aspect ratio. + float imageAspectRatio = (float) imageWidth / imageHeight; + float croppedWidth; + float croppedHeight; + if (screenAspectRatio < imageAspectRatio) { + croppedWidth = imageHeight * screenAspectRatio; + croppedHeight = imageHeight; + } else { + croppedWidth = imageWidth; + croppedHeight = imageWidth / screenAspectRatio; + } + + float u = (imageWidth - croppedWidth) / imageWidth * 0.5f; + float v = (imageHeight - croppedHeight) / imageHeight * 0.5f; + + float[] texCoordTransformed; + switch (cameraToDisplayRotation) { + case 90: + texCoordTransformed = new float[] {1 - u, 1 - v, u, 1 - v, 1 - u, v, u, v}; + break; + case 180: + texCoordTransformed = new float[] {1 - u, v, 1 - u, 1 - v, u, v, u, 1 - v}; + break; + case 270: + texCoordTransformed = new float[] {u, v, 1 - u, v, u, 1 - v, 1 - u, 1 - v}; + break; + case 0: + texCoordTransformed = new float[] {u, 1 - v, u, v, 1 - u, 1 - v, 1 - u, v}; + break; + default: + throw new IllegalArgumentException("Unhandled rotation: " + cameraToDisplayRotation); + } + + // Write image texture coordinates. + quadTexCoords.position(0); + quadTexCoords.put(texCoordTransformed); + + draw(); + } + + /** + * Draws the camera background image using the currently configured {@link + * BackgroundRenderer#quadTexCoords} image texture coordinates. + */ + private void draw() { + // Ensure position is rewound before use. + quadTexCoords.position(0); + + // No need to test or write depth, the screen quad has arbitrary depth, and is expected + // to be drawn first. + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + GLES20.glDepthMask(false); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); + + GLES20.glUseProgram(quadProgram); + + // Set the vertex positions. + GLES20.glVertexAttribPointer( + quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords); + + // Set the texture coordinates. + GLES20.glVertexAttribPointer( + quadTexCoordParam, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(quadPositionParam); + GLES20.glEnableVertexAttribArray(quadTexCoordParam); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Disable vertex arrays + GLES20.glDisableVertexAttribArray(quadPositionParam); + GLES20.glDisableVertexAttribArray(quadTexCoordParam); + + // Restore the depth state for further drawing. + GLES20.glDepthMask(true); + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + + ShaderUtil.checkGLError(TAG, "BackgroundRendererDraw"); + } + + private static final float[] QUAD_COORDS = + new float[] { + -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, + }; +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java new file mode 100644 index 0000000..9dfc0df --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ObjectRenderer.java @@ -0,0 +1,389 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.rendering; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.opengl.Matrix; +import de.javagl.obj.Obj; +import de.javagl.obj.ObjData; +import de.javagl.obj.ObjReader; +import de.javagl.obj.ObjUtils; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +/** Renders an object loaded from an OBJ file in OpenGL. */ +public class ObjectRenderer { + private static final String TAG = ObjectRenderer.class.getSimpleName(); + + /** + * Blend mode. + * + * @see #setBlendMode(BlendMode) + */ + public enum BlendMode { + /** Multiplies the destination color by the source alpha. */ + Shadow, + /** Normal alpha blending. */ + Grid + } + + // Shader names. + private static final String VERTEX_SHADER_NAME = "shaders/object.vert"; + private static final String FRAGMENT_SHADER_NAME = "shaders/object.frag"; + + private static final int COORDS_PER_VERTEX = 3; + private static final float[] DEFAULT_COLOR = new float[] {0f, 0f, 0f, 0f}; + + // Note: the last component must be zero to avoid applying the translational part of the matrix. + private static final float[] LIGHT_DIRECTION = new float[] {0.250f, 0.866f, 0.433f, 0.0f}; + private final float[] viewLightDirection = new float[4]; + + // Object vertex buffer variables. + private int vertexBufferId; + private int verticesBaseAddress; + private int texCoordsBaseAddress; + private int normalsBaseAddress; + private int indexBufferId; + private int indexCount; + + private int program; + private final int[] textures = new int[1]; + + // Shader location: model view projection matrix. + private int modelViewUniform; + private int modelViewProjectionUniform; + + // Shader location: object attributes. + private int positionAttribute; + private int normalAttribute; + private int texCoordAttribute; + + // Shader location: texture sampler. + private int textureUniform; + + // Shader location: environment properties. + private int lightingParametersUniform; + + // Shader location: material properties. + private int materialParametersUniform; + + // Shader location: color correction property + private int colorCorrectionParameterUniform; + + // Shader location: object color property (to change the primary color of the object). + private int colorUniform; + + private BlendMode blendMode = null; + + // Temporary matrices allocated here to reduce number of allocations for each frame. + private final float[] modelMatrix = new float[16]; + private final float[] modelViewMatrix = new float[16]; + private final float[] modelViewProjectionMatrix = new float[16]; + + // Set some default material properties to use for lighting. + private float ambient = 0.3f; + private float diffuse = 1.0f; + private float specular = 1.0f; + private float specularPower = 6.0f; + + public ObjectRenderer() {} + + /** + * Creates and initializes OpenGL resources needed for rendering the model. + * + * @param context Context for loading the shader and below-named model and texture assets. + * @param objAssetName Name of the OBJ file containing the model geometry. + * @param diffuseTextureAssetName Name of the PNG file containing the diffuse texture map. + */ + public void createOnGlThread(Context context, String objAssetName, String diffuseTextureAssetName) + throws IOException { + final int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); + final int fragmentShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); + + program = GLES20.glCreateProgram(); + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + GLES20.glUseProgram(program); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + modelViewUniform = GLES20.glGetUniformLocation(program, "u_ModelView"); + modelViewProjectionUniform = GLES20.glGetUniformLocation(program, "u_ModelViewProjection"); + + positionAttribute = GLES20.glGetAttribLocation(program, "a_Position"); + normalAttribute = GLES20.glGetAttribLocation(program, "a_Normal"); + texCoordAttribute = GLES20.glGetAttribLocation(program, "a_TexCoord"); + + textureUniform = GLES20.glGetUniformLocation(program, "u_Texture"); + + lightingParametersUniform = GLES20.glGetUniformLocation(program, "u_LightingParameters"); + materialParametersUniform = GLES20.glGetUniformLocation(program, "u_MaterialParameters"); + colorCorrectionParameterUniform = + GLES20.glGetUniformLocation(program, "u_ColorCorrectionParameters"); + colorUniform = GLES20.glGetUniformLocation(program, "u_ObjColor"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + + // Read the texture. + Bitmap textureBitmap = + BitmapFactory.decodeStream(context.getAssets().open(diffuseTextureAssetName)); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glGenTextures(textures.length, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0); + GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + textureBitmap.recycle(); + + ShaderUtil.checkGLError(TAG, "Texture loading"); + + // Read the obj file. + InputStream objInputStream = context.getAssets().open(objAssetName); + Obj obj = ObjReader.read(objInputStream); + + // Prepare the Obj so that its structure is suitable for + // rendering with OpenGL: + // 1. Triangulate it + // 2. Make sure that texture coordinates are not ambiguous + // 3. Make sure that normals are not ambiguous + // 4. Convert it to single-indexed data + obj = ObjUtils.convertToRenderable(obj); + + // OpenGL does not use Java arrays. ByteBuffers are used instead to provide data in a format + // that OpenGL understands. + + // Obtain the data from the OBJ, as direct buffers: + IntBuffer wideIndices = ObjData.getFaceVertexIndices(obj, 3); + FloatBuffer vertices = ObjData.getVertices(obj); + FloatBuffer texCoords = ObjData.getTexCoords(obj, 2); + FloatBuffer normals = ObjData.getNormals(obj); + + // Convert int indices to shorts for GL ES 2.0 compatibility + ShortBuffer indices = + ByteBuffer.allocateDirect(2 * wideIndices.limit()) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + while (wideIndices.hasRemaining()) { + indices.put((short) wideIndices.get()); + } + indices.rewind(); + + int[] buffers = new int[2]; + GLES20.glGenBuffers(2, buffers, 0); + vertexBufferId = buffers[0]; + indexBufferId = buffers[1]; + + // Load vertex buffer + verticesBaseAddress = 0; + texCoordsBaseAddress = verticesBaseAddress + 4 * vertices.limit(); + normalsBaseAddress = texCoordsBaseAddress + 4 * texCoords.limit(); + final int totalBytes = normalsBaseAddress + 4 * normals.limit(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, totalBytes, null, GLES20.GL_STATIC_DRAW); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, verticesBaseAddress, 4 * vertices.limit(), vertices); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, texCoordsBaseAddress, 4 * texCoords.limit(), texCoords); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, normalsBaseAddress, 4 * normals.limit(), normals); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + // Load index buffer + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId); + indexCount = indices.limit(); + GLES20.glBufferData( + GLES20.GL_ELEMENT_ARRAY_BUFFER, 2 * indexCount, indices, GLES20.GL_STATIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "OBJ buffer load"); + + Matrix.setIdentityM(modelMatrix, 0); + } + + /** + * Selects the blending mode for rendering. + * + * @param blendMode The blending mode. Null indicates no blending (opaque rendering). + */ + public void setBlendMode(BlendMode blendMode) { + this.blendMode = blendMode; + } + + /** + * Updates the object model matrix and applies scaling. + * + * @param modelMatrix A 4x4 model-to-world transformation matrix, stored in column-major order. + * @param scaleFactor A separate scaling factor to apply before the {@code modelMatrix}. + * @see android.opengl.Matrix + */ + public void updateModelMatrix(float[] modelMatrix, float scaleFactor) { + float[] scaleMatrix = new float[16]; + Matrix.setIdentityM(scaleMatrix, 0); + scaleMatrix[0] = scaleFactor; + scaleMatrix[5] = scaleFactor; + scaleMatrix[10] = scaleFactor; + Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0); + } + + /** + * Sets the surface characteristics of the rendered model. + * + * @param ambient Intensity of non-directional surface illumination. + * @param diffuse Diffuse (matte) surface reflectivity. + * @param specular Specular (shiny) surface reflectivity. + * @param specularPower Surface shininess. Larger values result in a smaller, sharper specular + * highlight. + */ + public void setMaterialProperties( + float ambient, float diffuse, float specular, float specularPower) { + this.ambient = ambient; + this.diffuse = diffuse; + this.specular = specular; + this.specularPower = specularPower; + } + + /** + * Draws the model. + * + * @param cameraView A 4x4 view matrix, in column-major order. + * @param cameraPerspective A 4x4 projection matrix, in column-major order. + * @param lightIntensity Illumination intensity. Combined with diffuse and specular material + * properties. + * @see #setBlendMode(BlendMode) + * @see #updateModelMatrix(float[], float) + * @see #setMaterialProperties(float, float, float, float) + * @see android.opengl.Matrix + */ + public void draw(float[] cameraView, float[] cameraPerspective, float[] colorCorrectionRgba) { + draw(cameraView, cameraPerspective, colorCorrectionRgba, DEFAULT_COLOR); + } + + public void draw( + float[] cameraView, + float[] cameraPerspective, + float[] colorCorrectionRgba, + float[] objColor) { + + ShaderUtil.checkGLError(TAG, "Before draw"); + + // Build the ModelView and ModelViewProjection matrices + // for calculating object position and light. + Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0); + Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0); + + GLES20.glUseProgram(program); + + // Set the lighting environment properties. + Matrix.multiplyMV(viewLightDirection, 0, modelViewMatrix, 0, LIGHT_DIRECTION, 0); + normalizeVec3(viewLightDirection); + GLES20.glUniform4f( + lightingParametersUniform, + viewLightDirection[0], + viewLightDirection[1], + viewLightDirection[2], + 1.f); + GLES20.glUniform4fv(colorCorrectionParameterUniform, 1, colorCorrectionRgba, 0); + + // Set the object color property. + GLES20.glUniform4fv(colorUniform, 1, objColor, 0); + + // Set the object material properties. + GLES20.glUniform4f(materialParametersUniform, ambient, diffuse, specular, specularPower); + + // Attach the object texture. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + GLES20.glUniform1i(textureUniform, 0); + + // Set the vertex attributes. + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId); + + GLES20.glVertexAttribPointer( + positionAttribute, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, verticesBaseAddress); + GLES20.glVertexAttribPointer(normalAttribute, 3, GLES20.GL_FLOAT, false, 0, normalsBaseAddress); + GLES20.glVertexAttribPointer( + texCoordAttribute, 2, GLES20.GL_FLOAT, false, 0, texCoordsBaseAddress); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + // Set the ModelViewProjection matrix in the shader. + GLES20.glUniformMatrix4fv(modelViewUniform, 1, false, modelViewMatrix, 0); + GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(positionAttribute); + GLES20.glEnableVertexAttribArray(normalAttribute); + GLES20.glEnableVertexAttribArray(texCoordAttribute); + + if (blendMode != null) { + GLES20.glDepthMask(false); + GLES20.glEnable(GLES20.GL_BLEND); + switch (blendMode) { + case Shadow: + // Multiplicative blending function for Shadow. + GLES20.glBlendFunc(GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA); + break; + case Grid: + // Grid, additive blending function. + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + break; + } + } + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId); + GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexCount, GLES20.GL_UNSIGNED_SHORT, 0); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + if (blendMode != null) { + GLES20.glDisable(GLES20.GL_BLEND); + GLES20.glDepthMask(true); + } + + // Disable vertex arrays + GLES20.glDisableVertexAttribArray(positionAttribute); + GLES20.glDisableVertexAttribArray(normalAttribute); + GLES20.glDisableVertexAttribArray(texCoordAttribute); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + ShaderUtil.checkGLError(TAG, "After draw"); + } + + private static void normalizeVec3(float[] v) { + float reciprocalLength = 1.0f / (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] *= reciprocalLength; + v[1] *= reciprocalLength; + v[2] *= reciprocalLength; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java new file mode 100644 index 0000000..0d22246 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PlaneRenderer.java @@ -0,0 +1,419 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.rendering; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.GLUtils; +import android.opengl.Matrix; +import com.google.ar.core.Camera; +import com.google.ar.core.Plane; +import com.google.ar.core.Pose; +import com.google.ar.core.TrackingState; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Renders the detected AR planes. */ +public class PlaneRenderer { + private static final String TAG = PlaneRenderer.class.getSimpleName(); + + // Shader names. + private static final String VERTEX_SHADER_NAME = "shaders/plane.vert"; + private static final String FRAGMENT_SHADER_NAME = "shaders/plane.frag"; + + private static final int BYTES_PER_FLOAT = Float.SIZE / 8; + private static final int BYTES_PER_SHORT = Short.SIZE / 8; + private static final int COORDS_PER_VERTEX = 3; // x, z, alpha + + private static final int VERTS_PER_BOUNDARY_VERT = 2; + private static final int INDICES_PER_BOUNDARY_VERT = 3; + private static final int INITIAL_BUFFER_BOUNDARY_VERTS = 64; + + private static final int INITIAL_VERTEX_BUFFER_SIZE_BYTES = + BYTES_PER_FLOAT * COORDS_PER_VERTEX * VERTS_PER_BOUNDARY_VERT * INITIAL_BUFFER_BOUNDARY_VERTS; + + private static final int INITIAL_INDEX_BUFFER_SIZE_BYTES = + BYTES_PER_SHORT + * INDICES_PER_BOUNDARY_VERT + * INDICES_PER_BOUNDARY_VERT + * INITIAL_BUFFER_BOUNDARY_VERTS; + + private static final float FADE_RADIUS_M = 0.25f; + private static final float DOTS_PER_METER = 10.0f; + private static final float EQUILATERAL_TRIANGLE_SCALE = (float) (1 / Math.sqrt(3)); + + // Using the "signed distance field" approach to render sharp lines and circles. + // {dotThreshold, lineThreshold, lineFadeSpeed, occlusionScale} + // dotThreshold/lineThreshold: red/green intensity above which dots/lines are present + // lineFadeShrink: lines will fade in between alpha = 1-(1/lineFadeShrink) and 1.0 + // occlusionShrink: occluded planes will fade out between alpha = 0 and 1/occlusionShrink + private static final float[] GRID_CONTROL = {0.2f, 0.4f, 2.0f, 1.5f}; + + private int planeProgram; + private final int[] textures = new int[1]; + + private int planeXZPositionAlphaAttribute; + + private int planeModelUniform; + private int planeNormalUniform; + private int planeModelViewProjectionUniform; + private int textureUniform; + private int lineColorUniform; + private int dotColorUniform; + private int gridControlUniform; + private int planeUvMatrixUniform; + + private FloatBuffer vertexBuffer = + ByteBuffer.allocateDirect(INITIAL_VERTEX_BUFFER_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + private ShortBuffer indexBuffer = + ByteBuffer.allocateDirect(INITIAL_INDEX_BUFFER_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + + // Temporary lists/matrices allocated here to reduce number of allocations for each frame. + private final float[] modelMatrix = new float[16]; + private final float[] modelViewMatrix = new float[16]; + private final float[] modelViewProjectionMatrix = new float[16]; + private final float[] planeColor = new float[] {1f, 1f, 1f, 1f}; + private final float[] planeAngleUvMatrix = + new float[4]; // 2x2 rotation matrix applied to uv coords. + + private final Map planeIndexMap = new HashMap<>(); + + public PlaneRenderer() {} + + /** + * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the + * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source and texture PNG. + * @param gridDistanceTextureName Name of the PNG file containing the grid texture. + */ + public void createOnGlThread(Context context, String gridDistanceTextureName) throws IOException { + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); + int passthroughShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); + + planeProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(planeProgram, vertexShader); + GLES20.glAttachShader(planeProgram, passthroughShader); + GLES20.glLinkProgram(planeProgram); + GLES20.glUseProgram(planeProgram); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + // Read the texture. + Bitmap textureBitmap = + BitmapFactory.decodeStream(context.getAssets().open(gridDistanceTextureName)); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glGenTextures(textures.length, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0); + GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + ShaderUtil.checkGLError(TAG, "Texture loading"); + + planeXZPositionAlphaAttribute = GLES20.glGetAttribLocation(planeProgram, "a_XZPositionAlpha"); + + planeModelUniform = GLES20.glGetUniformLocation(planeProgram, "u_Model"); + planeNormalUniform = GLES20.glGetUniformLocation(planeProgram, "u_Normal"); + planeModelViewProjectionUniform = + GLES20.glGetUniformLocation(planeProgram, "u_ModelViewProjection"); + textureUniform = GLES20.glGetUniformLocation(planeProgram, "u_Texture"); + lineColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_lineColor"); + dotColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_dotColor"); + gridControlUniform = GLES20.glGetUniformLocation(planeProgram, "u_gridControl"); + planeUvMatrixUniform = GLES20.glGetUniformLocation(planeProgram, "u_PlaneUvMatrix"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + } + + /** Updates the plane model transform matrix and extents. */ + private void updatePlaneParameters( + float[] planeMatrix, float extentX, float extentZ, FloatBuffer boundary) { + System.arraycopy(planeMatrix, 0, modelMatrix, 0, 16); + if (boundary == null) { + vertexBuffer.limit(0); + indexBuffer.limit(0); + return; + } + + // Generate a new set of vertices and a corresponding triangle strip index set so that + // the plane boundary polygon has a fading edge. This is done by making a copy of the + // boundary polygon vertices and scaling it down around center to push it inwards. Then + // the index buffer is setup accordingly. + boundary.rewind(); + int boundaryVertices = boundary.limit() / 2; + int numVertices; + int numIndices; + + numVertices = boundaryVertices * VERTS_PER_BOUNDARY_VERT; + // drawn as GL_TRIANGLE_STRIP with 3n-2 triangles (n-2 for fill, 2n for perimeter). + numIndices = boundaryVertices * INDICES_PER_BOUNDARY_VERT; + + if (vertexBuffer.capacity() < numVertices * COORDS_PER_VERTEX) { + int size = vertexBuffer.capacity(); + while (size < numVertices * COORDS_PER_VERTEX) { + size *= 2; + } + vertexBuffer = + ByteBuffer.allocateDirect(BYTES_PER_FLOAT * size) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + } + vertexBuffer.rewind(); + vertexBuffer.limit(numVertices * COORDS_PER_VERTEX); + + if (indexBuffer.capacity() < numIndices) { + int size = indexBuffer.capacity(); + while (size < numIndices) { + size *= 2; + } + indexBuffer = + ByteBuffer.allocateDirect(BYTES_PER_SHORT * size) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + } + indexBuffer.rewind(); + indexBuffer.limit(numIndices); + + // Note: when either dimension of the bounding box is smaller than 2*FADE_RADIUS_M we + // generate a bunch of 0-area triangles. These don't get rendered though so it works + // out ok. + float xScale = Math.max((extentX - 2 * FADE_RADIUS_M) / extentX, 0.0f); + float zScale = Math.max((extentZ - 2 * FADE_RADIUS_M) / extentZ, 0.0f); + + while (boundary.hasRemaining()) { + float x = boundary.get(); + float z = boundary.get(); + vertexBuffer.put(x); + vertexBuffer.put(z); + vertexBuffer.put(0.0f); + vertexBuffer.put(x * xScale); + vertexBuffer.put(z * zScale); + vertexBuffer.put(1.0f); + } + + // step 1, perimeter + indexBuffer.put((short) ((boundaryVertices - 1) * 2)); + for (int i = 0; i < boundaryVertices; ++i) { + indexBuffer.put((short) (i * 2)); + indexBuffer.put((short) (i * 2 + 1)); + } + indexBuffer.put((short) 1); + // This leaves us on the interior edge of the perimeter between the inset vertices + // for boundary verts n-1 and 0. + + // step 2, interior: + for (int i = 1; i < boundaryVertices / 2; ++i) { + indexBuffer.put((short) ((boundaryVertices - 1 - i) * 2 + 1)); + indexBuffer.put((short) (i * 2 + 1)); + } + if (boundaryVertices % 2 != 0) { + indexBuffer.put((short) ((boundaryVertices / 2) * 2 + 1)); + } + } + + private void draw(float[] cameraView, float[] cameraPerspective, float[] planeNormal) { + // Build the ModelView and ModelViewProjection matrices + // for calculating cube position and light. + Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0); + Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0); + + // Set the position of the plane + vertexBuffer.rewind(); + GLES20.glVertexAttribPointer( + planeXZPositionAlphaAttribute, + COORDS_PER_VERTEX, + GLES20.GL_FLOAT, + false, + BYTES_PER_FLOAT * COORDS_PER_VERTEX, + vertexBuffer); + + // Set the Model and ModelViewProjection matrices in the shader. + GLES20.glUniformMatrix4fv(planeModelUniform, 1, false, modelMatrix, 0); + GLES20.glUniform3f(planeNormalUniform, planeNormal[0], planeNormal[1], planeNormal[2]); + GLES20.glUniformMatrix4fv( + planeModelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0); + + indexBuffer.rewind(); + GLES20.glDrawElements( + GLES20.GL_TRIANGLE_STRIP, indexBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, indexBuffer); + ShaderUtil.checkGLError(TAG, "Drawing plane"); + } + + static class SortablePlane { + final float distance; + final Plane plane; + + SortablePlane(float distance, Plane plane) { + this.distance = distance; + this.plane = plane; + } + } + + /** + * Draws the collection of tracked planes, with closer planes hiding more distant ones. + * + * @param allPlanes The collection of planes to draw. + * @param cameraPose The pose of the camera, as returned by {@link Camera#getPose()} + * @param cameraPerspective The projection matrix, as returned by {@link + * Camera#getProjectionMatrix(float[], int, float, float)} + */ + public void drawPlanes(Collection allPlanes, Pose cameraPose, float[] cameraPerspective) { + // Planes must be sorted by distance from camera so that we draw closer planes first, and + // they occlude the farther planes. + List sortedPlanes = new ArrayList<>(); + + for (Plane plane : allPlanes) { + if (plane.getTrackingState() != TrackingState.TRACKING || plane.getSubsumedBy() != null) { + continue; + } + + float distance = calculateDistanceToPlane(plane.getCenterPose(), cameraPose); + if (distance < 0) { // Plane is back-facing. + continue; + } + sortedPlanes.add(new SortablePlane(distance, plane)); + } + Collections.sort( + sortedPlanes, + new Comparator() { + @Override + public int compare(SortablePlane a, SortablePlane b) { + return Float.compare(a.distance, b.distance); + } + }); + + float[] cameraView = new float[16]; + cameraPose.inverse().toMatrix(cameraView, 0); + + // Planes are drawn with additive blending, masked by the alpha channel for occlusion. + + // Start by clearing the alpha channel of the color buffer to 1.0. + GLES20.glClearColor(1, 1, 1, 1); + GLES20.glColorMask(false, false, false, true); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLES20.glColorMask(true, true, true, true); + + // Disable depth write. + GLES20.glDepthMask(false); + + // Additive blending, masked by alpha channel, clearing alpha channel. + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFuncSeparate( + GLES20.GL_DST_ALPHA, GLES20.GL_ONE, // RGB (src, dest) + GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA); // ALPHA (src, dest) + + // Set up the shader. + GLES20.glUseProgram(planeProgram); + + // Attach the texture. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + GLES20.glUniform1i(textureUniform, 0); + + // Shared fragment uniforms. + GLES20.glUniform4fv(gridControlUniform, 1, GRID_CONTROL, 0); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(planeXZPositionAlphaAttribute); + + ShaderUtil.checkGLError(TAG, "Setting up to draw planes"); + + for (SortablePlane sortedPlane : sortedPlanes) { + Plane plane = sortedPlane.plane; + float[] planeMatrix = new float[16]; + plane.getCenterPose().toMatrix(planeMatrix, 0); + + float[] normal = new float[3]; + // Get transformed Y axis of plane's coordinate system. + plane.getCenterPose().getTransformedAxis(1, 1.0f, normal, 0); + + updatePlaneParameters( + planeMatrix, plane.getExtentX(), plane.getExtentZ(), plane.getPolygon()); + + // Get plane index. Keep a map to assign same indices to same planes. + Integer planeIndex = planeIndexMap.get(plane); + if (planeIndex == null) { + planeIndex = planeIndexMap.size(); + planeIndexMap.put(plane, planeIndex); + } + + // Set plane color. + GLES20.glUniform4fv(lineColorUniform, 1, planeColor, 0); + GLES20.glUniform4fv(dotColorUniform, 1, planeColor, 0); + + // Each plane will have its own angle offset from others, to make them easier to + // distinguish. Compute a 2x2 rotation matrix from the angle. + float angleRadians = planeIndex * 0.144f; + float uScale = DOTS_PER_METER; + float vScale = DOTS_PER_METER * EQUILATERAL_TRIANGLE_SCALE; + planeAngleUvMatrix[0] = +(float) Math.cos(angleRadians) * uScale; + planeAngleUvMatrix[1] = -(float) Math.sin(angleRadians) * vScale; + planeAngleUvMatrix[2] = +(float) Math.sin(angleRadians) * uScale; + planeAngleUvMatrix[3] = +(float) Math.cos(angleRadians) * vScale; + GLES20.glUniformMatrix2fv(planeUvMatrixUniform, 1, false, planeAngleUvMatrix, 0); + + draw(cameraView, cameraPerspective, normal); + } + + // Clean up the state we set + GLES20.glDisableVertexAttribArray(planeXZPositionAlphaAttribute); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + GLES20.glDisable(GLES20.GL_BLEND); + GLES20.glDepthMask(true); + GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + + ShaderUtil.checkGLError(TAG, "Cleaning up after drawing planes"); + } + + // Calculate the normal distance to plane from cameraPose, the given planePose should have y axis + // parallel to plane's normal, for example plane's center pose or hit test pose. + public static float calculateDistanceToPlane(Pose planePose, Pose cameraPose) { + float[] normal = new float[3]; + float cameraX = cameraPose.tx(); + float cameraY = cameraPose.ty(); + float cameraZ = cameraPose.tz(); + // Get transformed Y axis of plane's coordinate system. + planePose.getTransformedAxis(1, 1.0f, normal, 0); + // Compute dot product of plane's normal with vector from camera to plane center. + return (cameraX - planePose.tx()) * normal[0] + + (cameraY - planePose.ty()) * normal[1] + + (cameraZ - planePose.tz()) * normal[2]; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java new file mode 100644 index 0000000..fb02149 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/PointCloudRenderer.java @@ -0,0 +1,152 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.rendering; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import com.google.ar.core.PointCloud; +import java.io.IOException; + +/** Renders a point cloud. */ +public class PointCloudRenderer { + private static final String TAG = PointCloud.class.getSimpleName(); + + // Shader names. + private static final String VERTEX_SHADER_NAME = "shaders/point_cloud.vert"; + private static final String FRAGMENT_SHADER_NAME = "shaders/point_cloud.frag"; + + private static final int BYTES_PER_FLOAT = Float.SIZE / 8; + private static final int FLOATS_PER_POINT = 4; // X,Y,Z,confidence. + private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * FLOATS_PER_POINT; + private static final int INITIAL_BUFFER_POINTS = 1000; + + private int vbo; + private int vboSize; + + private int programName; + private int positionAttribute; + private int modelViewProjectionUniform; + private int colorUniform; + private int pointSizeUniform; + + private int numPoints = 0; + + // Keep track of the last point cloud rendered to avoid updating the VBO if point cloud + // was not changed. Do this using the timestamp since we can't compare PointCloud objects. + private long lastTimestamp = 0; + + public PointCloudRenderer() {} + + /** + * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the + * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) throws IOException { + ShaderUtil.checkGLError(TAG, "before create"); + + int[] buffers = new int[1]; + GLES20.glGenBuffers(1, buffers, 0); + vbo = buffers[0]; + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + + vboSize = INITIAL_BUFFER_POINTS * BYTES_PER_POINT; + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "buffer alloc"); + + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); + int passthroughShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); + + programName = GLES20.glCreateProgram(); + GLES20.glAttachShader(programName, vertexShader); + GLES20.glAttachShader(programName, passthroughShader); + GLES20.glLinkProgram(programName); + GLES20.glUseProgram(programName); + + ShaderUtil.checkGLError(TAG, "program"); + + positionAttribute = GLES20.glGetAttribLocation(programName, "a_Position"); + colorUniform = GLES20.glGetUniformLocation(programName, "u_Color"); + modelViewProjectionUniform = GLES20.glGetUniformLocation(programName, "u_ModelViewProjection"); + pointSizeUniform = GLES20.glGetUniformLocation(programName, "u_PointSize"); + + ShaderUtil.checkGLError(TAG, "program params"); + } + + /** + * Updates the OpenGL buffer contents to the provided point. Repeated calls with the same point + * cloud will be ignored. + */ + public void update(PointCloud cloud) { + if (cloud.getTimestamp() == lastTimestamp) { + // Redundant call. + return; + } + ShaderUtil.checkGLError(TAG, "before update"); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + lastTimestamp = cloud.getTimestamp(); + + // If the VBO is not large enough to fit the new point cloud, resize it. + numPoints = cloud.getPoints().remaining() / FLOATS_PER_POINT; + if (numPoints * BYTES_PER_POINT > vboSize) { + while (numPoints * BYTES_PER_POINT > vboSize) { + vboSize *= 2; + } + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW); + } + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, 0, numPoints * BYTES_PER_POINT, cloud.getPoints()); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "after update"); + } + + /** + * Renders the point cloud. ARCore point cloud is given in world space. + * + * @param cameraView the camera view matrix for this frame, typically from {@link + * com.google.ar.core.Camera#getViewMatrix(float[], int)}. + * @param cameraPerspective the camera projection matrix for this frame, typically from {@link + * com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)}. + */ + public void draw(float[] cameraView, float[] cameraPerspective) { + float[] modelViewProjection = new float[16]; + Matrix.multiplyMM(modelViewProjection, 0, cameraPerspective, 0, cameraView, 0); + + ShaderUtil.checkGLError(TAG, "Before draw"); + + GLES20.glUseProgram(programName); + GLES20.glEnableVertexAttribArray(positionAttribute); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + GLES20.glVertexAttribPointer(positionAttribute, 4, GLES20.GL_FLOAT, false, BYTES_PER_POINT, 0); + GLES20.glUniform4f(colorUniform, 31.0f / 255.0f, 188.0f / 255.0f, 210.0f / 255.0f, 1.0f); + GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjection, 0); + GLES20.glUniform1f(pointSizeUniform, 5.0f); + + GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numPoints); + GLES20.glDisableVertexAttribArray(positionAttribute); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "Draw"); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java new file mode 100644 index 0000000..9ea2159 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/common/rendering/ShaderUtil.java @@ -0,0 +1,106 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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.ar.core.examples.java.common.rendering; + +import android.content.Context; +import android.opengl.GLES20; +import android.util.Log; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** Shader helper functions. */ +public class ShaderUtil { + /** + * Converts a raw text file, saved as a resource, into an OpenGL ES shader. + * + * @param type The type of shader we will be creating. + * @param filename The filename of the asset file about to be turned into a shader. + * @return The shader object handler. + */ + public static int loadGLShader(String tag, Context context, int type, String filename) + throws IOException { + String code = readShaderFileFromAssets(context, filename); + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, code); + GLES20.glCompileShader(shader); + + // Get the compilation status. + final int[] compileStatus = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); + + // If the compilation failed, delete the shader. + if (compileStatus[0] == 0) { + Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); + GLES20.glDeleteShader(shader); + shader = 0; + } + + if (shader == 0) { + throw new RuntimeException("Error creating shader."); + } + + return shader; + } + + /** + * Checks if we've had an error inside of OpenGL ES, and if so what that error is. + * + * @param label Label to report in case of error. + * @throws RuntimeException If an OpenGL error is detected. + */ + public static void checkGLError(String tag, String label) { + int lastError = GLES20.GL_NO_ERROR; + // Drain the queue of all errors. + int error; + while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { + Log.e(tag, label + ": glError " + error); + lastError = error; + } + if (lastError != GLES20.GL_NO_ERROR) { + throw new RuntimeException(label + ": glError " + lastError); + } + } + + /** + * Converts a raw shader file into a string. + * + * @param filename The filename of the shader file about to be turned into a shader. + * @return The context of the text file, or null in case of error. + */ + private static String readShaderFileFromAssets(Context context, String filename) + throws IOException { + try (InputStream inputStream = context.getAssets().open(filename); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + String[] tokens = line.split(" ", -1); + if (tokens[0].equals("#include")) { + String includeFilename = tokens[1]; + includeFilename = includeFilename.replace("\"", ""); + if (includeFilename.equals(filename)) { + throw new IOException("Do not include the calling file."); + } + sb.append(readShaderFileFromAssets(context, includeFilename)); + } else { + sb.append(line).append("\n"); + } + } + return sb.toString(); + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java new file mode 100644 index 0000000..57fe08d --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java @@ -0,0 +1,399 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * 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.ar.core.examples.java.helloar; + +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.Toast; +import com.google.ar.core.Anchor; +import com.google.ar.core.ArCoreApk; +import com.google.ar.core.Camera; +import com.google.ar.core.Frame; +import com.google.ar.core.HitResult; +import com.google.ar.core.Plane; +import com.google.ar.core.Point; +import com.google.ar.core.Point.OrientationMode; +import com.google.ar.core.PointCloud; +import com.google.ar.core.Session; +import com.google.ar.core.Trackable; +import com.google.ar.core.TrackingState; +import com.google.ar.core.examples.java.common.framework.RWT.RWTSurfaceView; +import com.google.ar.core.examples.java.common.helpers.CameraPermissionHelper; +import com.google.ar.core.examples.java.common.helpers.DisplayRotationHelper; +import com.google.ar.core.examples.java.common.helpers.FullScreenHelper; +import com.google.ar.core.examples.java.common.helpers.SnackbarHelper; +import com.google.ar.core.examples.java.common.helpers.TapHelper; +import com.google.ar.core.examples.java.common.helpers.TrackingStateHelper; +import com.google.ar.core.examples.java.common.rendering.BackgroundRenderer; +import com.google.ar.core.examples.java.common.rendering.ObjectRenderer; +import com.google.ar.core.examples.java.common.rendering.ObjectRenderer.BlendMode; +import com.google.ar.core.examples.java.common.rendering.PlaneRenderer; +import com.google.ar.core.examples.java.common.rendering.PointCloudRenderer; +import com.google.ar.core.exceptions.CameraNotAvailableException; +import com.google.ar.core.exceptions.UnavailableApkTooOldException; +import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException; +import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException; +import com.google.ar.core.exceptions.UnavailableSdkTooOldException; +import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException; +import java.io.IOException; +import java.util.ArrayList; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * This is a simple example that shows how to create an augmented reality (AR) application using the + * ARCore API. The application will display any detected planes and will allow the user to tap on a + * plane to place a 3d model of the Android robot. + */ +public class HelloArActivity extends AppCompatActivity implements GLSurfaceView.Renderer { + private static final String TAG = HelloArActivity.class.getSimpleName(); + + // Rendering. The Renderers are created here, and initialized when the GL surface is created. + private GLSurfaceView surfaceView; + + private boolean installRequested; + + private Session session; + private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper(); + private DisplayRotationHelper displayRotationHelper; + private final TrackingStateHelper trackingStateHelper = new TrackingStateHelper(this); + private TapHelper tapHelper; + + private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer(); + private final ObjectRenderer virtualObject = new ObjectRenderer(); + private final ObjectRenderer virtualObjectShadow = new ObjectRenderer(); + private final PlaneRenderer planeRenderer = new PlaneRenderer(); + private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer(); + + // Temporary matrix allocated here to reduce number of allocations for each frame. + private final float[] anchorMatrix = new float[16]; + private static final float[] DEFAULT_COLOR = new float[] {0f, 0f, 0f, 0f}; + + private static final String SEARCHING_PLANE_MESSAGE = "Searching for surfaces..."; + + // Anchors created from taps used for object placing with a given color. + private static class ColoredAnchor { + public final Anchor anchor; + public final float[] color; + + public ColoredAnchor(Anchor a, float[] color4f) { + this.anchor = a; + this.color = color4f; + } + } + + private final ArrayList anchors = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + surfaceView = findViewById(R.id.surfaceview); + displayRotationHelper = new DisplayRotationHelper(/*context=*/ this); + + // Set up tap listener. + tapHelper = new TapHelper(/*context=*/ this); + surfaceView.setOnTouchListener(tapHelper); + + // Set up renderer. + surfaceView.setPreserveEGLContextOnPause(true); + surfaceView.setEGLContextClientVersion(2); + surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending. + surfaceView.setRenderer(this); + surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + surfaceView.setWillNotDraw(false); + + installRequested = false; + } + + @Override + protected void onResume() { + super.onResume(); + + if (session == null) { + Exception exception = null; + String message = null; + try { + switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) { + case INSTALL_REQUESTED: + installRequested = true; + return; + case INSTALLED: + break; + } + + // ARCore requires camera permissions to operate. If we did not yet obtain runtime + // permission on Android M and above, now is a good time to ask the user for it. + if (!CameraPermissionHelper.hasCameraPermission(this)) { + CameraPermissionHelper.requestCameraPermission(this); + return; + } + + // Create the session. + session = new Session(/* context= */ this); + + } catch (UnavailableArcoreNotInstalledException + | UnavailableUserDeclinedInstallationException e) { + message = "Please install ARCore"; + exception = e; + } catch (UnavailableApkTooOldException e) { + message = "Please update ARCore"; + exception = e; + } catch (UnavailableSdkTooOldException e) { + message = "Please update this app"; + exception = e; + } catch (UnavailableDeviceNotCompatibleException e) { + message = "This device does not support AR"; + exception = e; + } catch (Exception e) { + message = "Failed to create AR session"; + exception = e; + } + + if (message != null) { + messageSnackbarHelper.showError(this, message); + Log.e(TAG, "Exception creating session", exception); + return; + } + } + + // Note that order matters - see the note in onPause(), the reverse applies here. + try { + session.resume(); + } catch (CameraNotAvailableException e) { + messageSnackbarHelper.showError(this, "Camera not available. Try restarting the app."); + session = null; + return; + } + + surfaceView.onResume(); + displayRotationHelper.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + if (session != null) { + // Note that the order matters - GLSurfaceView is paused first so that it does not try + // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may + // still call session.update() and get a SessionPausedException. + displayRotationHelper.onPause(); + surfaceView.onPause(); + session.pause(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) { + if (!CameraPermissionHelper.hasCameraPermission(this)) { + Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG) + .show(); + if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) { + // Permission denied with checking "Do not ask again". + CameraPermissionHelper.launchPermissionSettings(this); + } + finish(); + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + FullScreenHelper.setFullScreenOnWindowFocusChanged(this, hasFocus); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + + // Prepare the rendering objects. This involves reading shaders, so may throw an IOException. + try { + // Create the texture and pass it to ARCore session to be filled during update(). + backgroundRenderer.createOnGlThread(/*context=*/ this); + planeRenderer.createOnGlThread(/*context=*/ this, "models/trigrid.png"); + pointCloudRenderer.createOnGlThread(/*context=*/ this); + + virtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png"); + virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f); + + virtualObjectShadow.createOnGlThread( + /*context=*/ this, "models/andy_shadow.obj", "models/andy_shadow.png"); + virtualObjectShadow.setBlendMode(BlendMode.Shadow); + virtualObjectShadow.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f); + + } catch (IOException e) { + Log.e(TAG, "Failed to read an asset file", e); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + displayRotationHelper.onSurfaceChanged(width, height); + GLES20.glViewport(0, 0, width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + // Clear screen to notify driver it should not load any pixels from previous frame. + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + + if (session == null) { + return; + } + // Notify ARCore session that the view size changed so that the perspective matrix and + // the video background can be properly adjusted. + displayRotationHelper.updateSessionIfNeeded(session); + + try { + session.setCameraTextureName(backgroundRenderer.getTextureId()); + + // Obtain the current frame from ARSession. When the configuration is set to + // UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the + // camera framerate. + Frame frame = session.update(); + Camera camera = frame.getCamera(); + + // Handle one tap per frame. + handleTap(frame, camera); + + // If frame is ready, render camera preview image to the GL surface. + backgroundRenderer.draw(frame); + + // Keep the screen unlocked while tracking, but allow it to lock when tracking stops. + trackingStateHelper.updateKeepScreenOnFlag(camera.getTrackingState()); + + // If not tracking, don't draw 3D objects, show tracking failure reason instead. + if (camera.getTrackingState() == TrackingState.PAUSED) { + messageSnackbarHelper.showMessage( + this, TrackingStateHelper.getTrackingFailureReasonString(camera)); + return; + } + + // Get projection matrix. + float[] projmtx = new float[16]; + camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f); + + // Get camera matrix and draw. + float[] viewmtx = new float[16]; + camera.getViewMatrix(viewmtx, 0); + + // Compute lighting from average intensity of the image. + // The first three components are color scaling factors. + // The last one is the average pixel intensity in gamma space. + final float[] colorCorrectionRgba = new float[4]; + frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0); + + // Visualize tracked points. + // Use try-with-resources to automatically release the point cloud. + try (PointCloud pointCloud = frame.acquirePointCloud()) { + pointCloudRenderer.update(pointCloud); + pointCloudRenderer.draw(viewmtx, projmtx); + } + + // No tracking error at this point. If we detected any plane, then hide the + // message UI, otherwise show searchingPlane message. + if (hasTrackingPlane()) { + messageSnackbarHelper.hide(this); + } else { + messageSnackbarHelper.showMessage(this, SEARCHING_PLANE_MESSAGE); + } + + // Visualize planes. + planeRenderer.drawPlanes( + session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx); + + // Visualize anchors created by touch. + float scaleFactor = 1.0f; + for (ColoredAnchor coloredAnchor : anchors) { + if (coloredAnchor.anchor.getTrackingState() != TrackingState.TRACKING) { + continue; + } + // Get the current pose of an Anchor in world space. The Anchor pose is updated + // during calls to session.update() as ARCore refines its estimate of the world. + coloredAnchor.anchor.getPose().toMatrix(anchorMatrix, 0); + + // Update and draw the model and its shadow. + virtualObject.updateModelMatrix(anchorMatrix, scaleFactor); + virtualObjectShadow.updateModelMatrix(anchorMatrix, scaleFactor); + virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color); + virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color); + } + + } catch (Throwable t) { + // Avoid crashing the application due to unhandled exceptions. + Log.e(TAG, "Exception on the OpenGL thread", t); + } + } + + // Handle only one tap per frame, as taps are usually low frequency compared to frame rate. + private void handleTap(Frame frame, Camera camera) { + MotionEvent tap = tapHelper.poll(); + if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) { + for (HitResult hit : frame.hitTest(tap)) { + // Check if any plane was hit, and if it was hit inside the plane polygon + Trackable trackable = hit.getTrackable(); + // Creates an anchor if a plane or an oriented point was hit. + if ((trackable instanceof Plane + && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()) + && (PlaneRenderer.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) > 0)) + || (trackable instanceof Point + && ((Point) trackable).getOrientationMode() + == OrientationMode.ESTIMATED_SURFACE_NORMAL)) { + // Hits are sorted by depth. Consider only closest hit on a plane or oriented point. + // Cap the number of objects created. This avoids overloading both the + // rendering system and ARCore. + if (anchors.size() >= 20) { + anchors.get(0).anchor.detach(); + anchors.remove(0); + } + + // Assign a color to the object for rendering based on the trackable type + // this anchor attached to. For AR_TRACKABLE_POINT, it's blue color, and + // for AR_TRACKABLE_PLANE, it's green color. + float[] objColor; + if (trackable instanceof Point) { + objColor = new float[] {66.0f, 133.0f, 244.0f, 255.0f}; + } else if (trackable instanceof Plane) { + objColor = new float[] {139.0f, 195.0f, 74.0f, 255.0f}; + } else { + objColor = DEFAULT_COLOR; + } + + // Adding an Anchor tells ARCore that it should track this position in + // space. This anchor is created on the Plane to place the 3D model + // in the correct position relative both to the world and to the plane. + anchors.add(new ColoredAnchor(hit.createAnchor(), objColor)); + break; + } + } + } + } + + /** Checks if we detected at least one plane. */ + private boolean hasTrackingPlane() { + for (Plane plane : session.getAllTrackables(Plane.class)) { + if (plane.getTrackingState() == TrackingState.TRACKING) { + return true; + } + } + return false; + } +} diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..3f691da --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..2723fc1 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..4555298 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,19 @@ + + + + HelloAR Java + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..68b12b6 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..382bee5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + mavenLocal() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# 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/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..87b738c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar Binary files differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bcedaa3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Nov 20 10:27:45 PST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..af6708f --- /dev/null +++ b/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='"-Xmx64m"' + +# 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/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..6d57edc --- /dev/null +++ b/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="-Xmx64m" + +@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/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'