summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
authorJohn Hoford <hoford@google.com>2015-01-26 18:29:39 -0800
committerJohn Hoford <hoford@google.com>2015-02-06 16:50:13 -0800
commit5b539461dcc159bd89297443780d635ccc5e3564 (patch)
treed3834e17cd37a6dcda8b5aec91dfdf60bf4f1cbf /java/tests
parent841ba73b843babb8ca0117e9801b6db6ba6cd394 (diff)
downloadrs-5b539461dcc159bd89297443780d635ccc5e3564.tar.gz
intital checkin of the Volume Renderer
Change-Id: I618e3a44cc0cbd523b6dea745440342ffb0103da
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/VrDemo/Android.mk27
-rw-r--r--java/tests/VrDemo/AndroidManifest.xml37
-rw-r--r--java/tests/VrDemo/_index.html80
-rw-r--r--java/tests/VrDemo/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--java/tests/VrDemo/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 19388 bytes
-rw-r--r--java/tests/VrDemo/res/layout-land/activity_vr.xml111
-rw-r--r--java/tests/VrDemo/res/layout/activity_vr.xml111
-rw-r--r--java/tests/VrDemo/res/values-v21/styles.xml19
-rw-r--r--java/tests/VrDemo/res/values-w820dp/dimens.xml6
-rw-r--r--java/tests/VrDemo/res/values/dimens.xml19
-rw-r--r--java/tests/VrDemo/res/values/strings.xml29
-rw-r--r--java/tests/VrDemo/res/values/styles.xml22
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java204
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java478
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java70
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java110
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java322
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java368
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java56
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java161
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java97
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java208
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java125
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java66
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java369
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java124
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java127
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java126
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs206
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs169
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs306
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java620
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java152
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java76
-rw-r--r--java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java126
37 files changed, 5127 insertions, 0 deletions
diff --git a/java/tests/VrDemo/Android.mk b/java/tests/VrDemo/Android.mk
new file mode 100644
index 00000000..061af75b
--- /dev/null
+++ b/java/tests/VrDemo/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 21
+
+LOCAL_PACKAGE_NAME := VrDemo
+
+include $(BUILD_PACKAGE)
diff --git a/java/tests/VrDemo/AndroidManifest.xml b/java/tests/VrDemo/AndroidManifest.xml
new file mode 100644
index 00000000..9f5930ce
--- /dev/null
+++ b/java/tests/VrDemo/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="rsexample.google.com.vrdemo" >
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application
+ android:largeHeap="true"
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.rs.vr.VrActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/java/tests/VrDemo/_index.html b/java/tests/VrDemo/_index.html
new file mode 100644
index 00000000..51f54802
--- /dev/null
+++ b/java/tests/VrDemo/_index.html
@@ -0,0 +1,80 @@
+<h1>A Volume Renderer</h1>
+<h2>To setup to load DICOM or RAW data</h2>
+<p>
+<ol>
+ <li>Create directory <b>/sdcard/Download/volumes/</b></li>
+ <li>Create subdirectories for each dataset</li>
+ <li>Create a ".prop" for each data set</li>
+</ol>
+
+".prop" files are simple jave properties files which consist
+of key=value pairs
+
+<dl>
+ <dt>name</dt>
+ <dd>The name to be desplayed on the screen</dd>
+ <dt>dir</dt>
+ <dd>The subdirectory containing the data</dd>
+ <dt>format</dt>
+ <dd>the format the data is in (raw or dicom)</dd>
+ <dt>format</dt>
+ <dd>The format of the pixes (only short & ushort supported)</dd>
+ <dt>dim</dt>
+ <dd>the X by Y by Z dimensions of the data seperated by x</dd>
+ <dt>voxeldim</dt>
+ <dd>The size of each voxel separated by a comma "," uniform = (1.0,1.0,1.0)</dd>
+ <dt>sequence</dt>
+ <dd>if it says 1file the raw data is all on one file</dd>
+ <dt>looks</dt>
+ <dd>The name of the supplied looks</dd>
+ <dt>XXXXX.opacity</dt>
+ <dd>the opacity table asociate with the XXXXX look</dd>
+ <dt>XXXXX.color</dt>
+ <dd>The color table asociated with the XXXXX look</dd>
+</dl>
+</p>
+<h3>Opacity table</h3>
+opacity tabls consisit of nested arrays of shorts.
+The first being intensity the second being opacity on a 0-255 scale.
+The values for each pixel in the volume are interpolated
+<p></p>
+<h3>Color table</h3>
+The color table
+The values are linearly interpolated from one value to the next
+
+<dl>
+ <dt>intensity</dt>
+ <dd>The intensity that has this "color"</dd>
+ <dt>RGB</dt>
+ <dd>Hex number describing the color this number has</dd>
+ <dt>diffuse</dt>
+ <dd>how much difuse lighting to apply to this materal 0-100</dd>
+ <dt>ambient</dt>
+ <dd>how much ambient lighting does this materal reflect 0-100</dd>
+ <dt>specular</dt>
+ <dd>How much specular reflection materal has 0-100</dd>
+</dl>
+<p></p>
+<h3>Example property file</h3>
+<p>
+<PRE>
+name=Back Pack
+dir=backpack
+format=raw
+endian=little
+pixels=ushort
+dim=512x512x373
+voxeldim=0.9766, 0.9766, 1.25
+sequence=1file
+looks=clean,clay,black
+black.opacity = { {255, 0},{259, 255}};
+black.color = {{255, 0xff0000, 10, 10, 20},{259, 0xfff5e4, 10, 10, 20}};
+clay.opacity = { {27, 0}, {92, 0}, {629, 0}, {1203, 255}, {2343, 255}}
+clay.color = {{27, 0xe1ac96, 30, 70, 20}, {92, 0xbc703a, 30, 70, 20}, {629, 0xe29c9d, 30, 70, 20}, {1203, 0xe1af97, 30, 34, 20}, {2343, 0xdebd9d, 30, 38, 20}}
+clean.opacity = {{27, 0}, {92, 0}, {1433, 0}, {1691, 255}, {1734, 255}}
+clean.color = {{27, 0xe1ac96, 30, 70, 20}, {92, 0xe1ac96, 30, 70, 20}, {629, 0xed1904, 30, 70, 0}, {1611, 0xaeb3c9, 19, 42, 44}, {1826, 0xe7e7ea, 19, 44, 41}}
+</PRE>
+
+
+ </p>
+
diff --git a/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 00000000..96a442e5
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 00000000..359047df
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..71c6d760
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4df18946
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/layout-land/activity_vr.xml b/java/tests/VrDemo/res/layout-land/activity_vr.xml
new file mode 100644
index 00000000..3ab5662f
--- /dev/null
+++ b/java/tests/VrDemo/res/layout-land/activity_vr.xml
@@ -0,0 +1,111 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".VrActivity">
+ <view
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ class="com.example.android.rs.vr.VrView"
+ android:id="@+id/view"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/title"
+ android:text="@string/volume_name"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1.0"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/xcut"
+ android:textOff="@string/xcut"
+ android:onClick="cutXClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOff="@string/ycut"
+ android:textOn="@string/ycut"
+ android:onClick="cutYClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/zcut"
+ android:textOff="@string/zcut"
+ android:onClick="cutZClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/rotate"
+ android:textOff="@string/rotate"
+ android:onClick="rotateClick"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/looks"
+ android:onClick="looksClick"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/load"
+ android:onClick="dataClick"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/reset"
+ android:onClick="resetClick" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/save"
+ android:onClick="saveClick" />
+ </LinearLayout>
+
+ </LinearLayout>
+ </LinearLayout>
diff --git a/java/tests/VrDemo/res/layout/activity_vr.xml b/java/tests/VrDemo/res/layout/activity_vr.xml
new file mode 100644
index 00000000..d121f4f2
--- /dev/null
+++ b/java/tests/VrDemo/res/layout/activity_vr.xml
@@ -0,0 +1,111 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".VrActivity">
+ <view
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ class="com.example.android.rs.vr.VrView"
+ android:id="@+id/view"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/title"
+ android:text="@string/volume_name"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1.0"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/xcut"
+ android:textOff="@string/xcut"
+ android:onClick="cutXClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOff="@string/ycut"
+ android:textOn="@string/ycut"
+ android:onClick="cutYClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/zcut"
+ android:textOff="@string/zcut"
+ android:onClick="cutZClick" />
+ <ToggleButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/rotate"
+ android:textOff="@string/rotate"
+ android:onClick="rotateClick"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/looks"
+ android:onClick="looksClick"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/load"
+ android:onClick="dataClick"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/reset"
+ android:onClick="resetClick" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/save"
+ android:onClick="saveClick" />
+ </LinearLayout>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/tests/VrDemo/res/values-v21/styles.xml b/java/tests/VrDemo/res/values-v21/styles.xml
new file mode 100644
index 00000000..6e7e1016
--- /dev/null
+++ b/java/tests/VrDemo/res/values-v21/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <style name="AppTheme" parent="android:Theme.Material.Light">
+ </style>
+</resources>
diff --git a/java/tests/VrDemo/res/values-w820dp/dimens.xml b/java/tests/VrDemo/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..63fc8164
--- /dev/null
+++ b/java/tests/VrDemo/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/java/tests/VrDemo/res/values/dimens.xml b/java/tests/VrDemo/res/values/dimens.xml
new file mode 100644
index 00000000..248dfff2
--- /dev/null
+++ b/java/tests/VrDemo/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/java/tests/VrDemo/res/values/strings.xml b/java/tests/VrDemo/res/values/strings.xml
new file mode 100644
index 00000000..accf51eb
--- /dev/null
+++ b/java/tests/VrDemo/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <string name="app_name">Volume Rendering</string>
+ <string name="rotate">Rotate</string>
+ <string name="save">save</string>
+ <string name="reset">Reset</string>
+ <string name="load">Load</string>
+ <string name="looks">Looks</string>
+ <string name="zcut">Z Cut</string>
+ <string name="ycut">Y Cut</string>
+ <string name="xcut">X Cut</string>
+ <string name="volume_name">Volume Name</string>
+
+</resources>
diff --git a/java/tests/VrDemo/res/values/styles.xml b/java/tests/VrDemo/res/values/styles.xml
new file mode 100644
index 00000000..0f1c956c
--- /dev/null
+++ b/java/tests/VrDemo/res/values/styles.xml
@@ -0,0 +1,22 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java b/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java
new file mode 100644
index 00000000..aa64d2ac
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.renderscript.RenderScript;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.PopupMenu;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import java.io.File;
+
+import rsexample.google.com.vrdemo.R;
+
+import com.example.android.rs.vr.engine.Volume;
+import com.example.android.rs.vr.engine.VrState;
+import com.example.android.rs.vr.loaders.VolumeLoader;
+
+/**
+ * basic activity loads the volume and sets it on the VrView
+ */
+public class VrActivity extends Activity {
+ private static final String LOGTAG = "VrActivity";
+ VrState mState = new VrState();
+ VrView mVrView;
+ VolumeLoader mLoader;
+ private RenderScript mRs;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_vr);
+
+ mVrView = (VrView) findViewById(R.id.view);
+ mRs = RenderScript.create(VrActivity.this);
+
+ String dir = "/sdcard/Download/volumes";
+ mLoader = new VolumeLoader(dir);
+ VrSetupTask setup = new VrSetupTask();
+ String [] names = mLoader.getNames();
+ setup.execute(names[0]);
+ TextView tv = (TextView) findViewById(R.id.title);
+ tv.setText(names[0]);
+ }
+
+ class VrSetupTask extends AsyncTask<String, Integer, Volume> {
+ ProgressDialog progressDialog;
+
+ protected void onPreExecute() {
+ super.onPreExecute();
+ progressDialog = new ProgressDialog(VrActivity.this);
+ progressDialog.setMessage( "Loading Volume");
+ progressDialog.setIndeterminate(true);
+ progressDialog.setCancelable(false);
+ progressDialog.setProgress(0);
+ progressDialog.setMax(100);
+ progressDialog.show();
+
+ mLoader.setProgressListener(new VolumeLoader.ProgressListener() {
+ @Override
+ public void progress(int n, int total) {
+ publishProgress(n, total);
+ }
+ });
+ }
+
+ @Override
+ protected Volume doInBackground(String... names) {
+ return mLoader.getVolume(mRs, names[0]);
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ progressDialog.setMessage("load "+progress[0]+"/"+progress[1]);
+ progressDialog.setMax(progress[1]);
+ progressDialog.setProgress(progress[0]);
+ Log.v(LOGTAG,"Loading "+ progress[0]+"/"+progress[1]);
+ }
+
+ protected void onPostExecute(Volume v) {
+ Log.v(LOGTAG,"done");
+ mVrView.setVolume(mRs, v);
+ progressDialog.dismiss();
+ }
+ }
+
+ public void cutXClick(View v) {
+ mVrView.setMode(VrView.CUT_X_MODE);
+ uncheckOthers(v);
+ }
+
+ public void cutYClick(View v) {
+ mVrView.setMode(VrView.CUT_Y_MODE);
+ uncheckOthers(v);
+ }
+
+ public void cutZClick(View v) {
+ mVrView.setMode(VrView.CUT_Z_MODE);
+ uncheckOthers(v);
+ }
+
+ public void rotateClick(View v) {
+ mVrView.setMode(VrView.ROTATE_MODE);
+ uncheckOthers(v);
+ }
+
+ public void resetClick(View v) {
+ mVrView.resetCut();
+ }
+
+ public void saveClick(View v) {
+ // TODO should save and Image
+ }
+
+ public void looksClick(View v) {
+ String[] looks = mVrView.getLooks();
+ PopupMenu popup = new PopupMenu(this, v);
+ Menu menu = popup.getMenu();
+
+ for (int i = 0; i < looks.length; i++) {
+ menu.add(0, Menu.FIRST + i, Menu.NONE, looks[i]);
+ }
+
+ //registering popup with OnMenuItemClickListener
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ mVrView.setLook(item.getTitle().toString());
+ return true;
+ }
+ });
+ popup.show();
+ uncheckOthers(v);
+ }
+
+ public void dataClick(View v) {
+ Log.v(LOGTAG, "dataClick");
+
+ String[] volumes = mLoader.getNames();
+ PopupMenu popup = new PopupMenu(this, v);
+ Menu menu = popup.getMenu();
+
+ for (int i = 0; i < volumes.length; i++) {
+ menu.add(0, Menu.FIRST + i, Menu.NONE, volumes[i]);
+ }
+
+ //registering popup with OnMenuItemClickListener
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+
+ VrSetupTask setup = new VrSetupTask();
+ String title = item.getTitle().toString();
+ TextView tv = (TextView) findViewById(R.id.title);
+ tv.setText(title);
+ setup.execute(title);
+ return true;
+ }
+ });
+
+ popup.show();
+ uncheckOthers(v);
+ }
+
+ private void uncheckOthers(View v) {
+ ViewGroup p = (ViewGroup) v.getParent().getParent();
+ uncheckOthers(p, v);
+ }
+
+ private void uncheckOthers(ViewGroup p, View v) {
+ int n = p.getChildCount();
+ for (int i = 0; i < n; i++) {
+ final View child = p.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ uncheckOthers((ViewGroup) child, v);
+ }
+ if (v != child && child instanceof ToggleButton) {
+ ToggleButton t = (ToggleButton) child;
+ t.setChecked(false);
+
+ }
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java
new file mode 100644
index 00000000..b4ce67a1
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
+import android.renderscript.RenderScript;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.TextureView;
+
+import com.example.android.rs.vr.engine.Cube;
+import com.example.android.rs.vr.engine.Pipeline;
+import com.example.android.rs.vr.engine.RsBrickedBitMask;
+import com.example.android.rs.vr.engine.TriData;
+import com.example.android.rs.vr.engine.VectorUtil;
+import com.example.android.rs.vr.engine.ViewMatrix;
+import com.example.android.rs.vr.engine.Volume;
+import com.example.android.rs.vr.engine.VrPipline1;
+import com.example.android.rs.vr.engine.VrState;
+
+import java.util.Arrays;
+
+/**
+ * VrView runs a volume rendering on the screen
+ */
+public class VrView extends TextureView {
+ private static final String LOGTAG = "rsexample.google.com.vrdemo";
+ private Pipeline mPipline = new VrPipline1();//BasicPipline();
+ // private VrState mState4 = new VrState(); // for down sampled
+ private VrState mState1 = new VrState(); // for full res version
+ private VrState mStateLow = new VrState(); // for full res version
+ private VrState mLastDrawn = new VrState(); // for full res version
+ private Paint paint = new Paint();
+ private SurfaceTexture mSurfaceTexture;
+ private Surface mSurface;
+ ///private Size mImageViewSize;
+ private int refresh = 0; // 0 is no refresh else refresh = downsample
+ int mPreviousMode = -1;
+ int last_look = 0;
+
+ // int mDownSample = 4;
+ private final char[] looks = {
+ ViewMatrix.UP_AT,
+ ViewMatrix.DOWN_AT,
+ ViewMatrix.RIGHT_AT,
+ ViewMatrix.LEFT_AT,
+ ViewMatrix.FORWARD_AT,
+ ViewMatrix.BEHIND_AT};
+ private byte mMode = ROTATE_MODE;
+ private ScaleGestureDetector mScaleDetector;
+ private boolean mInScale;
+
+ public static final byte ROTATE_MODE = 1;
+ public static final byte CUT_X_MODE = 2;
+ public static final byte CUT_Y_MODE = 3;
+ public static final byte CUT_Z_MODE = 4;
+
+ public void setMode(byte mode) {
+ mMode = mode;
+ }
+
+ private float mDownPointX;
+ private float mDownPointY;
+ private double mDownScreenWidth;
+ private double[] mDownLookPoint = new double[3];
+ private double[] mDownEyePoint = new double[3];
+ private double[] mDownUpVector = new double[3];
+ private double[] mDownRightVector = new double[3];
+ private float[] mCurrentTrim = new float[6];
+ VrRenderTesk mRenderTesk;
+ VrBinGridTask mBinGridTask;
+
+ public VrView(Context context) {
+ super(context);
+ setup(context);
+ paint.setFilterBitmap(true);
+ }
+
+ public VrView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setup(context);
+ }
+
+
+ public VrView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setup(context);
+ }
+
+
+ public VrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setup(context);
+ }
+
+ private void setup(Context context) {
+ setBackgroundColor(Color.BLACK);
+ if (isInEditMode()) {
+ return;
+ }
+ setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ mSurfaceTexture = surface;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ mSurfaceTexture = surface;
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+ });
+
+ mScaleDetector = new ScaleGestureDetector(context,
+ new ScaleGestureDetector.OnScaleGestureListener() {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ double width = mState1.mTransform.getScreenWidth() / detector.getScaleFactor();
+ mState1.mTransform.setScreenWidth(width);
+ panMove(detector.getFocusX(), detector.getFocusY());
+ render(4);
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ panDown(detector.getFocusX(), detector.getFocusY());
+ mInScale = true;
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ mInScale = false;
+ }
+ });
+ }
+
+ private void updateOutputDimensions(int width, int height) {
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ if (mPreviousMode == 1) {
+ mPipline.cancel();
+ }
+ boolean handled = mScaleDetector.onTouchEvent(e);
+ if (e.getPointerCount() > 1) {
+ return true;
+ }
+ if (mInScale) {
+ return true;
+ }
+ int action = e.getAction();
+ if (action == MotionEvent.ACTION_DOWN) {
+ actionDown(e);
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ actionMove(e);
+ render(4);
+ } else if (action == MotionEvent.ACTION_UP) {
+ actionUp(e);
+ refresh = 1;
+ render(1);
+ }
+ return true;
+ }
+
+ private void panMove(float x, float y) {
+ double dist_x = (mDownPointX - x) * mDownScreenWidth / getWidth();
+ double dist_y = (y - mDownPointY) * mDownScreenWidth / getWidth();
+ double[] p;
+ p = mState1.mTransform.getEyePoint();
+ p[0] = mDownEyePoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0];
+ p[1] = mDownEyePoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1];
+ p[2] = mDownEyePoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2];
+ mState1.mTransform.setEyePoint(p);
+ p = mState1.mTransform.getLookPoint();
+ p[0] = mDownLookPoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0];
+ p[1] = mDownLookPoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1];
+ p[2] = mDownLookPoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2];
+ mState1.mTransform.setLookPoint(p);
+ }
+
+ private void panDown(float x, float y) {
+ mDownPointX = x;
+ mDownPointY = y;
+ mDownScreenWidth = mState1.mTransform.getScreenWidth();
+ double[] p;
+ p = mState1.mTransform.getLookPoint();
+ System.arraycopy(p, 0, mDownLookPoint, 0, 3);
+ p = mState1.mTransform.getEyePoint();
+ System.arraycopy(p, 0, mDownEyePoint, 0, 3);
+ p = mState1.mTransform.getUpVector();
+ System.arraycopy(p, 0, mDownUpVector, 0, 3);
+ mDownRightVector[0] = mDownLookPoint[0] - mDownEyePoint[0];
+ mDownRightVector[1] = mDownLookPoint[1] - mDownEyePoint[1];
+ mDownRightVector[2] = mDownLookPoint[2] - mDownEyePoint[2];
+ VectorUtil.normalize(mDownRightVector);
+ VectorUtil.cross(mDownRightVector, mDownUpVector, mDownRightVector);
+ }
+
+ private void actionDown(MotionEvent e) {
+ panDown(e.getX(), e.getY());
+
+ switch (mMode) {
+ case ROTATE_MODE:
+ mState1.mTransform.trackBallDown(e.getX(), e.getY());
+ break;
+
+ case CUT_X_MODE:
+ case CUT_Y_MODE:
+ case CUT_Z_MODE:
+ float[] trim = mState1.mCubeVolume.getTrim();
+ System.arraycopy(trim, 0, mCurrentTrim, 0, 6);
+ break;
+ }
+ }
+
+ private void actionMove(MotionEvent e) {
+ float deltax, deltay;
+
+ switch (mMode) {
+ case ROTATE_MODE:
+
+ mState1.mTransform.trackBallMove(e.getX(), e.getY());
+
+ break;
+
+ case CUT_X_MODE:
+ deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+ deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+ cut(0, deltax, deltay);
+ break;
+ case CUT_Y_MODE:
+ deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+ deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+ cut(1, deltax, deltay);
+ break;
+ case CUT_Z_MODE:
+ deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+ deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+ cut(2, deltax, deltay);
+ break;
+
+ }
+ }
+
+ private void actionUp(MotionEvent e) {
+ }
+
+ public void cut(int side, float fractionx, float fractiony) {
+ float[] f = Arrays.copyOf(mCurrentTrim, mCurrentTrim.length);
+ f[side] += fractionx;
+ if (f[side] < 0) f[side] = 0;
+ if (f[side] > .8) f[side] = .8f;
+ f[side + 3] += fractiony;
+ if (f[side + 3] < 0) f[side + 3] = 0;
+ if (f[side + 3] > .8) f[side + 3] = .8f;
+ mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, f);
+ mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+ mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim);
+ }
+
+ public void resetCut() {
+ Arrays.fill(mCurrentTrim, 0);
+ mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, mCurrentTrim);
+ mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+ mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim);
+ mState1.mTransform.look(looks[last_look], mState1.mCubeScreen, getWidth(), getHeight());
+ mState1.mTransform.setScreenWidth(.6f * mState1.mTransform.getScreenWidth());
+ last_look = (last_look + 1) % looks.length;
+ render(4);
+ }
+
+ public void setVolume(RenderScript rs, Volume v) {
+ mState1.mRs = rs;
+ mState1.mVolume = v;
+ mState1.mCubeVolume = new Cube(mState1.mVolume, 5f);
+ mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+ mState1.mCubeScreen.scale(v.mVoxelDim);
+ mState1.mTransform.setVoxelDim(v.mVoxelDim);
+ mState1.mTransform.look(ViewMatrix.DOWN_AT, mState1.mCubeScreen, getWidth(), getHeight());
+ setLook(mState1.mVolume.getLookNames()[0]);
+ }
+
+ protected void look(int k) {
+ mState1.mTransform.look(looks[k], mState1.mCubeVolume, getWidth(), getHeight());
+ render(4);
+ render(1);
+ }
+
+ void render(int downSample) {
+
+ if (mRenderTesk == null) {
+ mRenderTesk = new VrRenderTesk();
+ refresh = 0;
+ mRenderTesk.execute(downSample);
+ } else {
+ refresh = downSample;
+ }
+ }
+
+ public String[] getLooks() {
+ return mState1.mVolume.getLookNames();
+ }
+
+ public void setLook(String look) {
+ int[][] color = mState1.mVolume.getLookColor(look);
+ int[][] opacity = mState1.mVolume.getLookOpactiy(look);
+ mState1.mMaterial.setup(opacity, color);
+ if (mBinGridTask == null) {
+ mBinGridTask = new VrBinGridTask();
+ mBinGridTask.execute(mState1.mVolume);
+ }
+ }
+
+ class VrRenderTesk extends AsyncTask<Integer, String, Long> {
+
+ long m_last_time;
+
+ @Override
+ protected void onPreExecute() {
+ mStateLow.copyData(mState1);
+ }
+
+ @Override
+ protected void onCancelled() {
+ mPipline.cancel();
+ }
+
+ @Override
+ protected Long doInBackground(Integer... down) {
+ if (mState1.mRs == null) return 0L;
+ if (mSurfaceTexture == null) return 0L;
+ int sample = 4;
+ VrState state = mStateLow;
+ if (down[0] == 1) {
+ if (mPreviousMode == 4) {
+ mState1.copyData(mLastDrawn);
+ } else {
+ mState1.copyData(mStateLow);
+ }
+ // mStateLow.mScrAllocation.setSurface(null);
+ state = mState1;
+ sample = 1;
+ if (mStateLow.mScrAllocation != null) {
+ mStateLow.mScrAllocation.setSurface(null);
+ }
+ } else {
+ if (mState1.mScrAllocation != null) {
+ mState1.mScrAllocation.setSurface(null);
+ }
+ }
+
+ if (mPreviousMode != sample) {
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = new Surface(mSurfaceTexture);
+ }
+ mPreviousMode = sample;
+
+ int img_width = getWidth() / sample;
+ int img_height = getHeight() / sample;
+ state.createOutputAllocation(mSurface, img_width, img_height);
+
+ mPipline.initBuffers(state);
+
+ if (mPipline.isCancel()) {
+ return 0L;
+ }
+ long start = System.nanoTime();
+ addTimeLine(null);
+ mPipline.setupTriangles(state);
+
+ if (mPipline.isCancel()) {
+ return 0L;
+ }
+ mPipline.rasterizeTriangles(state);
+
+ if (mPipline.isCancel()) {
+ return 0L;
+ }
+ mPipline.raycast(state);
+
+ if (mPipline.isCancel()) {
+ return 0L;
+ }
+ mLastDrawn.copyData(state);
+ state.mRs.finish();
+ state.mScrAllocation.ioSend();
+
+ long time = System.nanoTime();
+ addLine("vr(" + img_width + "," + img_height + "): " + (time - start) / 1E6f + " ms");
+ return 0L;
+ }
+
+ private void addTimeLine(String line) {
+ if (line == null) {
+ m_last_time = System.nanoTime();
+ return;
+ }
+ long time = System.nanoTime();
+ float ftime = (time - m_last_time) / 1E6f;
+ if (ftime > 100)
+ addLine(line + ": " + (ftime / 1E3f) + " sec");
+ else
+ addLine(line + ": " + (ftime) + " ms");
+ m_last_time = System.nanoTime();
+ }
+
+ private void addLine(String line) {
+ publishProgress(line);
+ }
+
+ protected void onProgressUpdate(String... progress) {
+ Log.v(LOGTAG, progress[0]);
+ }
+
+ protected void onPostExecute(Long result) {
+ invalidate();
+ mRenderTesk = null;
+ if (refresh != 0) {
+ render(refresh);
+ }
+ }
+ }
+
+ class VrBinGridTask extends AsyncTask<Volume, String, Long> {
+
+ @Override
+ protected Long doInBackground(Volume... v) {
+ mState1.mRsMask = new RsBrickedBitMask(mState1);
+ mState1.mRs.finish();
+ return 0L;
+ }
+
+ protected void onProgressUpdate(String... progress) {
+ Log.v(LOGTAG, progress[0]);
+ }
+
+ protected void onPostExecute(Long result) {
+ mBinGridTask = null;
+ render(4);
+ render(1);
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java
new file mode 100644
index 00000000..6cec0b01
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+/**
+ * Base implementation of a rendering pipeline Simply renders a box
+ */
+public class BasicPipeline implements Pipeline {
+ boolean mCancel = false;
+ private static final String LOGTAG = "BasicPipeline";
+ ScriptC_rasterize scriptC_rasterize;
+
+ @Override
+ public void cancel() {
+ mCancel = true;
+ }
+
+ @Override
+ public boolean isCancel() {
+ return mCancel;
+ }
+
+ @Override
+ public void initBuffers(VrState state) {
+ mCancel = false;
+ }
+
+ @Override
+ public void setupTriangles(VrState state) {
+ Matrix m = state.mTransform.getMatrix(Transform.VOLUME_SPACE, Transform.SCREEN_SPACE);
+ state.mCubeVolume.transform(m, state.mCubeScreen);
+ }
+
+ @Override
+ public void rasterizeTriangles(VrState state) {
+ if (scriptC_rasterize == null) {
+ scriptC_rasterize = new ScriptC_rasterize(state.mRs);
+ }
+ scriptC_rasterize.set_index(state.mCubeScreen.mIndex);
+ scriptC_rasterize.set_vert(state.mCubeScreen.mVert);
+ long start = System.nanoTime();
+ scriptC_rasterize.invoke_setup_triangles(state.mImgWidth, state.mImgHeight);
+ if (mCancel) return;
+ scriptC_rasterize.forEach_render_z(state.mzRangeFullAllocation);
+ state.mRs.finish();
+ Log.v(LOGTAG,"render triangles "+((System.nanoTime()-start)/1E6f)+" ms");
+ }
+
+ @Override
+ public void raycast(VrState state) {
+ scriptC_rasterize.set_z_range_buff(state.mzRangeFullAllocation);
+ scriptC_rasterize.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation);
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java
new file mode 100644
index 00000000..040b86f8
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+import java.text.*;
+import java.util.Arrays;
+
+public class Cube extends TriData {
+
+ private float[] mTrim = {0, 0, 0, 0, 0, 0 };
+
+ public Cube(Volume v, float delta) {
+ this(v, delta, new float[]{0, 0, 0, 0, 0, 0});
+ }
+
+ public Cube(Volume v, float delta, float[] trim) {
+ this();
+ this.mTrim = trim;
+ float minx = delta + trim[0] * (v.mDimx - delta);
+ float miny = delta + trim[1] * (v.mDimy - delta);
+ float minz = delta + trim[2] * (v.mDimz - delta);
+ float maxx = v.mDimx - delta - trim[3] * (v.mDimx - delta);
+ float maxy = v.mDimy - delta - trim[4] * (v.mDimy - delta);
+ float maxz = v.mDimz - delta - trim[5] * (v.mDimz - delta);
+ mVert = new float[]{
+ minx, miny, minz,
+ maxx, miny, minz,
+ maxx, maxy, minz,
+ minx, maxy, minz,
+ minx, miny, maxz,
+ maxx, miny, maxz,
+ maxx, maxy, maxz,
+ minx, maxy, maxz,
+ };
+ }
+
+ public void clone(Cube src) {
+ System.arraycopy(src.mTrim, 0, mTrim, 0, mTrim.length);
+ mVert = Arrays.copyOf(src.mVert, src.mVert.length);
+ mIndex = Arrays.copyOf(src.mIndex, src.mIndex.length);
+ }
+
+ public float[] getTrim() {
+ return mTrim;
+ }
+
+ @Override
+ public String toString() {
+ return "CUBE[" + fs(mVert, 0, 3) + "][" + fs(mVert, 18, 3) + "]";
+ }
+
+ private static String fs(float[] f, int off, int n) {
+ DecimalFormat df = new DecimalFormat("##0.000");
+ String ret = "";
+ for (int i = off; i < off + n; i++) {
+ String s = " " + df.format(f[i]);
+
+ if (i != off) {
+ ret += ",";
+ }
+ ret += s.substring(s.length() - 8);
+
+ }
+ return ret;
+ }
+
+ public Cube() {
+ mVert = new float[]{
+ -1.f, -1.f, -1.f,
+ 1.f, -1.f, -1.f,
+ 1.f, 1.f, -1.f,
+ -1.f, 1.f, -1.f,
+ -1.f, -1.f, 1.f,
+ 1.f, -1.f, 1.f,
+ 1.f, 1.f, 1.f,
+ -1.f, 1.f, 1.f,
+ };
+
+ mIndex = new int[]{
+ 2, 1, 0,
+ 0, 3, 2,
+ 7, 4, 5,
+ 5, 6, 7,
+ 1, 2, 6,
+ 6, 5, 1,
+ 4, 7, 3,
+ 3, 0, 4,
+ 2, 3, 7,
+ 7, 6, 2,
+ 0, 1, 5,
+ 5, 4, 0
+ };
+ for (int i = 0; i < mIndex.length; i++) {
+ mIndex[i] *= 3;
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java
new file mode 100644
index 00000000..8517754a
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+
+/**
+ * Defines the material properties of a pixel value
+ * RGB, Opacity diffuse specular, ambient
+ */
+public class Material {
+ public static final int SIZE = 64 * 1024;
+ public static final int STRIDE = 8;
+
+ Allocation mOpacityAllocation;
+ Allocation mColorMapAllocation;
+
+ public byte[] mOpacityTable = new byte[SIZE];
+ MaterialProp[] mMaterialProp = new MaterialProp[0];
+ public byte[] mColor = new byte[SIZE * STRIDE]; // table contain r, g, b, A, S, D
+ public static final int RED = 0;
+ public static final int GREEN = 1;
+ public static final int BLUE = 2;
+ public static final int DIFF = 4;
+ public static final int SPEC = 5;
+ public static final int AMB = 6;
+
+ public static class MaterialProp {
+ int mPos;
+ int mRed;
+ int mGreen;
+ int mBlue;
+ float mDiffuse;
+ float mSpecular;
+ float mAmbient;
+ }
+
+ /**
+ * Clamp limits to less than or equal to 255
+ * this is done with a very efficient bit fiddling trick
+ * @param c value being clamped
+ * @return values in the range 0-255
+ */
+ private static int clamp(int c) {
+ final int N = 255; // the value clamp is limiting to
+ c &= ~(c >> 31);
+ c -= N;
+ c &= (c >> 31);
+ c += N;
+ return c;
+ }
+
+ public static class Opactiy {
+ int mPos;
+ float mValue;
+ }
+
+ public Opactiy[] mOpacity = new Opactiy[0];
+
+ public Material() {
+ simpleSetup(1150, 1300);
+ }
+
+ public void simpleSetup(int start, int end) {
+ float diffuse = .7f;
+ float specular = .0f;
+ float ambient = .3f;
+ for (int i = Short.MIN_VALUE; i < Short.MAX_VALUE; i++) {
+ int off = i & 0xFFFF;
+ int p = STRIDE * (off);
+ byte v = (byte) clamp((int) (255 * (i - start) / (end - start)));
+ mColor[p + RED] = v;
+ mColor[p + GREEN] = v;
+ mColor[p + BLUE] = v;
+ mColor[p + DIFF] = (byte) (255 * diffuse);
+ mColor[p + SPEC] = (byte) (255 * specular);
+ mColor[p + AMB] = (byte) (255 * ambient);
+ mOpacityTable[off] = v;
+ }
+ }
+
+ public void setup(int[] means, int start, int end) {
+ int[] pos = new int[means.length - 1];
+ int[] red = new int[means.length - 1];
+ int[] green = new int[means.length - 1];
+ int[] blue = new int[means.length - 1];
+
+ for (int i = 0; i < pos.length; i++) {
+ pos[i] = (means[i] + means[i + 1]) / 2;
+ float f = i / (float) (pos.length - 1);
+ float h = 1 - f * f * f;
+ float s = (float) (1 / (1 + Math.exp((f - .5) * 5)));
+ int rgb = Color.HSVToColor(new float[]{360 * h, s, f});
+ red[i] = (rgb >> 16) & 0xff;
+ green[i] = (rgb >> 8) & 0xff;
+ blue[i] = (rgb >> 0) & 0xff;
+ }
+ mMaterialProp = new MaterialProp[pos.length];
+
+ float diffuse = .7f;
+ float specular = .0f;
+ float ambient = .3f;
+ for (int i = 0; i < pos.length; i++) {
+ mMaterialProp[i] = new MaterialProp();
+ mMaterialProp[i].mAmbient = ambient;
+ mMaterialProp[i].mSpecular = specular;
+ mMaterialProp[i].mDiffuse = diffuse;
+ mMaterialProp[i].mPos = pos[i];
+ float t = i / (float) (pos.length - 1);
+
+ mMaterialProp[i].mRed = red[i];
+ mMaterialProp[i].mGreen = green[i];
+ mMaterialProp[i].mBlue = blue[i];
+
+ }
+ mOpacity = new Opactiy[2];
+ mOpacity[0] = new Opactiy();
+ mOpacity[0].mPos = start;
+ mOpacity[0].mValue = 0;
+
+ mOpacity[1] = new Opactiy();
+ mOpacity[1].mPos = end;
+ mOpacity[1].mValue = 1;
+
+ buildOpacityTable();
+ buildMaterialProp();
+ }
+
+ public void setup(int[][] opacity, int[][] material) {
+ mMaterialProp = new MaterialProp[material.length];
+
+ for (int i = 0; i < material.length; i++) {
+ int rgb = material[i][1] & 0xFFFFFF;
+
+ float ambient = (material[i].length > 2) ? material[i][2] / 100.f : .2f;
+ float diffuse = (material[i].length > 3) ? material[i][3] / 100.f : .6f;
+ float specular = (material[i].length > 4) ? material[i][4] / 100.f : .2f;
+
+ mMaterialProp[i] = new MaterialProp();
+ mMaterialProp[i].mAmbient = ambient;
+ mMaterialProp[i].mSpecular = specular;
+ mMaterialProp[i].mDiffuse = diffuse;
+ mMaterialProp[i].mRed = (rgb >> 16) & 0xff;
+ mMaterialProp[i].mGreen = (rgb >> 8) & 0xff;
+ mMaterialProp[i].mBlue = (rgb >> 0) & 0xff;
+
+ mMaterialProp[i].mPos = material[i][0];
+
+ }
+ mOpacity = new Opactiy[opacity.length];
+ for (int i = 0; i < opacity.length; i++) {
+ mOpacity[i] = new Opactiy();
+ mOpacity[i].mPos = opacity[i][0];
+ mOpacity[i].mValue = opacity[i][1] / 255.f;
+ }
+ buildOpacityTable();
+ buildMaterialProp();
+ }
+
+ public void setup(int start, int end) {
+ int[] pos = {1050, 1140, 1200, 1210, 1231};
+
+ mMaterialProp = new MaterialProp[pos.length];
+
+ float diffuse = .7f;
+ float specular = .0f;
+ float ambient = .3f;
+ for (int i = 0; i < pos.length; i++) {
+ mMaterialProp[i] = new MaterialProp();
+ mMaterialProp[i].mAmbient = ambient;
+ mMaterialProp[i].mSpecular = specular;
+ mMaterialProp[i].mDiffuse = diffuse;
+ mMaterialProp[i].mPos = pos[i];
+ float t = i / (float) (pos.length - 1);
+ int rgb = (int) (Math.random() * 0xFFFFFF);
+ mMaterialProp[i].mRed = (rgb >> 16) & 0xff;
+ mMaterialProp[i].mGreen = (rgb >> 8) & 0xff;
+ mMaterialProp[i].mBlue = (rgb >> 0) & 0xff;
+ }
+ mOpacity = new Opactiy[2];
+ mOpacity[0] = new Opactiy();
+ mOpacity[0].mPos = start;
+ mOpacity[0].mValue = 0;
+
+ mOpacity[1] = new Opactiy();
+ mOpacity[1].mPos = end;
+ mOpacity[1].mValue = 1;
+
+ buildOpacityTable();
+ buildMaterialProp();
+ }
+
+ void buildOpacityTable() {
+ if (mOpacity.length == 0) {
+ return;
+ }
+ for (int i = Short.MIN_VALUE; i <= mOpacity[0].mPos; i++) {
+ int p = i & 0xFFFF;
+ mOpacityTable[p] = (byte) (mOpacity[0].mValue * 255);
+ }
+ for (int k = 0; k < mOpacity.length - 1; k++) {
+ for (int i = mOpacity[k].mPos; i < mOpacity[k + 1].mPos; i++) {
+ int p = i & 0xFFFF;
+ float dist = mOpacity[k + 1].mPos - mOpacity[k].mPos;
+ float t = (i - mOpacity[k].mPos) / dist;
+ float v = mOpacity[k].mValue * (1 - t) + t * mOpacity[k + 1].mValue;
+ mOpacityTable[p] = (byte) (v * 255);
+ }
+ }
+ int last = mOpacity.length - 1;
+ for (int i = mOpacity[last].mPos; i <= Short.MAX_VALUE; i++) {
+ int p = i & 0xFFFF;
+ mOpacityTable[p] = (byte) (mOpacity[last].mValue * 255);
+
+ }
+ }
+
+ public void buildMaterialProp() {
+ MaterialProp[] m = mMaterialProp;
+ if (m.length == 0) {
+ return;
+ }
+ {
+ MaterialProp mp = m[0];
+ int red = m[0].mRed;
+ int green = m[0].mGreen;
+ int blue = m[0].mBlue;
+
+ for (int i = Short.MIN_VALUE; i <= m[0].mPos; i++) {
+ int p = STRIDE * (i & 0xFFFF);
+ mColor[p + RED] = (byte) red;
+ mColor[p + GREEN] = (byte) green;
+ mColor[p + BLUE] = (byte) blue;
+
+ mColor[p + DIFF] = (byte) (255 * mp.mDiffuse);
+ mColor[p + SPEC] = (byte) (255 * mp.mSpecular);
+ mColor[p + AMB] = (byte) (255 * mp.mAmbient);
+ }
+ }
+ for (int k = 0; k < m.length - 1; k++) {
+ for (int i = m[k].mPos; i < m[k + 1].mPos; i++) {
+ int p = STRIDE * (i & 0xFFFF);
+ float dist = m[k + 1].mPos - m[k].mPos;
+ float t2 = (i - m[k].mPos) / dist;
+ float t1 = 1 - t2;
+
+
+ int red = (int) (m[k].mRed * t1 + m[k + 1].mRed * t2);
+ int green = (int) (m[k].mGreen * t1 + m[k + 1].mGreen * t2);
+ int blue = (int) (m[k].mBlue * t1 + m[k + 1].mBlue * t2);
+
+ float diffuse = m[k].mDiffuse * t1 + m[k + 1].mDiffuse * t2;
+ float specular = m[k].mSpecular * t1 + m[k + 1].mSpecular * t2;
+ float ambient = m[k].mAmbient * t1 + m[k + 1].mAmbient * t2;
+
+
+ mColor[p + RED] = (byte) red;
+ mColor[p + GREEN] = (byte) green;
+ mColor[p + BLUE] = (byte) blue;
+
+ mColor[p + DIFF] = (byte) (255 * diffuse);
+ mColor[p + SPEC] = (byte) (255 * specular);
+ mColor[p + AMB] = (byte) (255 * ambient);
+ }
+ }
+ {
+ int last = m.length - 1;
+ MaterialProp mp = m[last];
+ int red = mp.mRed;
+ int green = mp.mGreen;
+ int blue = mp.mBlue;
+ for (int i = mp.mPos; i <= Short.MAX_VALUE; i++) {
+ int p = STRIDE * (i & 0xFFFF);
+ mColor[p + RED] = (byte) red;
+ mColor[p + GREEN] = (byte) green;
+ mColor[p + BLUE] = (byte) blue;
+
+ mColor[p + DIFF] = (byte) (255 * mp.mDiffuse);
+ mColor[p + SPEC] = (byte) (255 * mp.mSpecular);
+ mColor[p + AMB] = (byte) (255 * mp.mAmbient);
+ }
+ }
+ }
+
+ public Allocation getOpacityAllocation(RenderScript rs) {
+ if (mOpacityAllocation == null) {
+ Type.Builder b = new Type.Builder(rs, Element.U8(rs));
+ b.setX(mOpacityTable.length);
+ mOpacityAllocation = Allocation.createTyped(rs, b.create());
+ }
+ mOpacityAllocation.copyFromUnchecked(mOpacityTable);
+ return mOpacityAllocation;
+ }
+
+ public Allocation getColorMapAllocation(RenderScript rs) {
+ if (mColorMapAllocation == null) {
+ Type.Builder b = new Type.Builder(rs, Element.U8_4(rs));
+ b.setX(mColor.length / 4);
+ mColorMapAllocation = Allocation.createTyped(rs, b.create());
+ }
+ mColorMapAllocation.copyFromUnchecked(mColor);
+ return mColorMapAllocation;
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java
new file mode 100644
index 00000000..e178aee9
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+import java.text.*;
+import java.util.*;
+
+/**
+ * Generic matrix code
+ * Written for maximum portability between desktop and Android
+ * Not in performance critical sections
+ */
+public class Matrix {
+
+ private static final String LOGTAG = "Matrix";
+ public double[] m;
+
+ public void makeRotation() {
+ {
+ double[] v = {m[0], m[4], m[8]};
+ VectorUtil.normalize(v);
+ m[0] = v[0];
+ m[4] = v[1];
+ m[8] = v[2];
+ }
+ {
+ double[] v = {m[1], m[5], m[9]};
+ VectorUtil.normalize(v);
+ m[1] = v[0];
+ m[5] = v[1];
+ m[9] = v[2];
+ }
+ {
+ double[] v = {m[2], m[6], m[10]};
+ VectorUtil.normalize(v);
+ m[2] = v[0];
+ m[6] = v[1];
+ m[10] = v[2];
+ }
+
+ }
+
+ private static String trim(String s) {
+ return s.substring(s.length() - 7);
+ }
+
+ public void print() {
+ DecimalFormat df = new DecimalFormat(" ##0.000");
+ for (int i = 0; i < 4; i++) {
+ String s="";
+ for (int j = 0; j < 4; j++) {
+ s+= ((j == 0) ? "[ " : " , ") + trim(" "+df.format(m[i * 4 + j]));
+ }
+ Log.v(LOGTAG, s+"]");
+ }
+ }
+
+ public Matrix() {
+ m = new double[4 * 4];
+ setToUnit();
+ }
+
+ public Matrix(Matrix matrix) {
+ this(Arrays.copyOf(matrix.m, matrix.m.length));
+ }
+
+ protected Matrix(double[] m) {
+ this.m = m;
+ }
+
+ public void setToUnit() {
+ for (int i = 1; i < m.length; i++) {
+ m[i] = 0;
+ }
+ m[0] = 1;
+ m[5] = 1;
+ m[10] = 1;
+ m[15] = 1;
+ }
+
+ public void mult4(float[] src, float[] dest) {
+ for (int i = 0; i < 4; i++) {
+ int col = i * 4;
+ double sum = 0;
+ for (int j = 0; j < 4; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public void mult3(float[] src, float[] dest) {
+ for (int i = 0; i < 3; i++) {
+ int col = i * 4;
+ double sum = m[col + 3];
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public void mult3v(float[] src, float[] dest) {
+ for (int i = 0; i < 3; i++) {
+ int col = i * 4;
+ double sum = 0;
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public void mult4(double[] src, double[] dest) {
+ for (int i = 0; i < 4; i++) {
+ int col = i * 4;
+ double sum = 0;
+ for (int j = 0; j < 4; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public void mult3(double[] src, double[] dest) {
+ for (int i = 0; i < 3; i++) {
+ int col = i * 4;
+ double sum = m[col + 3];
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public void mult3v(double[] src, double[] dest) {
+ for (int i = 0; i < 3; i++) {
+ int col = i * 4;
+ double sum = 0;
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j];
+ }
+ dest[i] = (float) sum;
+ }
+ }
+
+ public double[] vecmult(double[] src) {
+ double[] ret = new double[3];
+ mult3v(src, ret);
+ return ret;
+ }
+
+ public void mult3(float[] src, int off1, float[] dest, int off2) {
+
+ int col = 0 * 4;
+ double sum = m[col + 3];
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j + off1];
+ }
+ float v0 = (float) sum;
+
+
+ col = 1 * 4;
+ sum = m[col + 3];
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j + off1];
+ }
+
+ float v1 = (float) sum;
+
+
+ col = 2 * 4;
+ sum = m[col + 3];
+ for (int j = 0; j < 3; j++) {
+ sum += m[col + j] * src[j + off1];
+ }
+ float v2 = (float) sum;
+
+ dest[off2] = v0;
+ dest[1 + off2] = v1;
+ dest[2 + off2] = v2;
+
+ }
+
+ public Matrix invers() {
+ double[] inv = new double[16];
+
+ inv[0] = m[5] * m[10] * m[15] -
+ m[5] * m[11] * m[14] -
+ m[9] * m[6] * m[15] +
+ m[9] * m[7] * m[14] +
+ m[13] * m[6] * m[11] -
+ m[13] * m[7] * m[10];
+
+ inv[4] = -m[4] * m[10] * m[15] +
+ m[4] * m[11] * m[14] +
+ m[8] * m[6] * m[15] -
+ m[8] * m[7] * m[14] -
+ m[12] * m[6] * m[11] +
+ m[12] * m[7] * m[10];
+
+ inv[8] = m[4] * m[9] * m[15] -
+ m[4] * m[11] * m[13] -
+ m[8] * m[5] * m[15] +
+ m[8] * m[7] * m[13] +
+ m[12] * m[5] * m[11] -
+ m[12] * m[7] * m[9];
+
+ inv[12] = -m[4] * m[9] * m[14] +
+ m[4] * m[10] * m[13] +
+ m[8] * m[5] * m[14] -
+ m[8] * m[6] * m[13] -
+ m[12] * m[5] * m[10] +
+ m[12] * m[6] * m[9];
+
+ inv[1] = -m[1] * m[10] * m[15] +
+ m[1] * m[11] * m[14] +
+ m[9] * m[2] * m[15] -
+ m[9] * m[3] * m[14] -
+ m[13] * m[2] * m[11] +
+ m[13] * m[3] * m[10];
+
+ inv[5] = m[0] * m[10] * m[15] -
+ m[0] * m[11] * m[14] -
+ m[8] * m[2] * m[15] +
+ m[8] * m[3] * m[14] +
+ m[12] * m[2] * m[11] -
+ m[12] * m[3] * m[10];
+
+ inv[9] = -m[0] * m[9] * m[15] +
+ m[0] * m[11] * m[13] +
+ m[8] * m[1] * m[15] -
+ m[8] * m[3] * m[13] -
+ m[12] * m[1] * m[11] +
+ m[12] * m[3] * m[9];
+
+ inv[13] = m[0] * m[9] * m[14] -
+ m[0] * m[10] * m[13] -
+ m[8] * m[1] * m[14] +
+ m[8] * m[2] * m[13] +
+ m[12] * m[1] * m[10] -
+ m[12] * m[2] * m[9];
+
+ inv[2] = m[1] * m[6] * m[15] -
+ m[1] * m[7] * m[14] -
+ m[5] * m[2] * m[15] +
+ m[5] * m[3] * m[14] +
+ m[13] * m[2] * m[7] -
+ m[13] * m[3] * m[6];
+
+ inv[6] = -m[0] * m[6] * m[15] +
+ m[0] * m[7] * m[14] +
+ m[4] * m[2] * m[15] -
+ m[4] * m[3] * m[14] -
+ m[12] * m[2] * m[7] +
+ m[12] * m[3] * m[6];
+
+ inv[10] = m[0] * m[5] * m[15] -
+ m[0] * m[7] * m[13] -
+ m[4] * m[1] * m[15] +
+ m[4] * m[3] * m[13] +
+ m[12] * m[1] * m[7] -
+ m[12] * m[3] * m[5];
+
+ inv[14] = -m[0] * m[5] * m[14] +
+ m[0] * m[6] * m[13] +
+ m[4] * m[1] * m[14] -
+ m[4] * m[2] * m[13] -
+ m[12] * m[1] * m[6] +
+ m[12] * m[2] * m[5];
+
+ inv[3] = -m[1] * m[6] * m[11] +
+ m[1] * m[7] * m[10] +
+ m[5] * m[2] * m[11] -
+ m[5] * m[3] * m[10] -
+ m[9] * m[2] * m[7] +
+ m[9] * m[3] * m[6];
+
+ inv[7] = m[0] * m[6] * m[11] -
+ m[0] * m[7] * m[10] -
+ m[4] * m[2] * m[11] +
+ m[4] * m[3] * m[10] +
+ m[8] * m[2] * m[7] -
+ m[8] * m[3] * m[6];
+
+ inv[11] = -m[0] * m[5] * m[11] +
+ m[0] * m[7] * m[9] +
+ m[4] * m[1] * m[11] -
+ m[4] * m[3] * m[9] -
+ m[8] * m[1] * m[7] +
+ m[8] * m[3] * m[5];
+
+ inv[15] = m[0] * m[5] * m[10] -
+ m[0] * m[6] * m[9] -
+ m[4] * m[1] * m[10] +
+ m[4] * m[2] * m[9] +
+ m[8] * m[1] * m[6] -
+ m[8] * m[2] * m[5];
+
+
+ double det;
+ det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
+
+ if (det == 0) {
+ return null;
+ }
+
+ det = 1.0 / det;
+
+ double[] out = new double[16];
+
+ for (int i = 0; i < 16; i++) {
+ out[i] = inv[i] * det;
+ }
+
+ Matrix ret = new Matrix(out);
+ return ret;
+ }
+
+ public void getAsFloats(float[] fm) {
+ for (int y = 0; y < 4; y++) {
+ int col = y * 4;
+
+ for (int x = 0; x < 4; x++)
+ fm[y + x * 4] = (float) m[x + y * 4];
+ }
+ }
+
+ Matrix mult(Matrix b) {
+ return new Matrix(multiply(this.m, b.m));
+ }
+
+ Matrix premult(Matrix b) {
+ return new Matrix(multiply(b.m, this.m));
+ }
+
+ private static double[] multiply(double a[], double b[]) {
+ double[] resultant = new double[16];
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ resultant[i + 4 * j] += a[i + 4 * k] * b[k + 4 * j];
+ }
+ }
+ }
+
+ return resultant;
+ }
+
+ public void clone(Matrix src) {
+ System.arraycopy(src.m, 0, m, 0, m.length);
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java
new file mode 100644
index 00000000..92546e01
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+/**
+ * The interface to the vr rendering pipeline
+ * This defines the various rendering stages that the system goes through
+ */
+public interface Pipeline {
+ /**
+ * cancel the current execution
+ */
+ public void cancel();
+
+ /**
+ * @return true if the rendering was canceled the output is not valid if true
+ */
+ public boolean isCancel();
+
+ /**
+ * The pipline uses this stage to setup buffers it needs
+ * @param state
+ */
+ public void initBuffers(VrState state);
+
+ /**
+ * This stage does needed transformations on the triangles to screen space
+ * @param state
+ */
+ public void setupTriangles(VrState state);
+
+ /**
+ * This stage converts triangles into buffers used in the raycast
+ * @param state
+ */
+ public void rasterizeTriangles(VrState state);
+
+ /**
+ * This stage generates the final image
+ * @param state
+ */
+ public void raycast(VrState state);
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java
new file mode 100644
index 00000000..bc0a0aef
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+/**
+ * Generic Quaternion
+ * Written for maximum portability between desktop and Android
+ * Not in performance critical sections
+ */
+public class Quaternion {
+ private final double[] x = new double[4]; // w,x,y,z,
+
+ public void set(double w, double x, double y, double z) {
+ this.x[0] = w;
+ this.x[1] = x;
+ this.x[2] = y;
+ this.x[3] = z;
+ }
+
+ public void clone(Quaternion src) {
+ System.arraycopy(src.x, 0, x, 0, x.length);
+ }
+
+ private static double[] cross(double[] a, double[] b) {
+ double out0 = a[1] * b[2] - b[1] * a[2];
+ double out1 = a[2] * b[0] - b[2] * a[0];
+ double out2 = a[0] * b[1] - b[0] * a[1];
+ return new double[]{out0, out1, out2};
+ }
+
+ private static double dot(double[] a, double[] b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+ }
+
+ private static double[] normal(double[] a) {
+ double norm = Math.sqrt(dot(a, a));
+ return new double[]{a[0] / norm, a[1] / norm, a[2] / norm};
+ }
+
+ public void set(double[] v1, double[] v2) {
+ double[] vec1 = normal(v1);
+ double[] vec2 = normal(v2);
+ double[] axis = normal(cross(vec1, vec2));
+ double angle = Math.acos(dot(vec1, vec2));
+ set(angle, axis);
+ }
+
+ public static double calcAngle(double[] v1, double[] v2) {
+ double[] vec1 = normal(v1);
+ double[] vec2 = normal(v2);
+ return Math.acos(Math.min(dot(vec1, vec2), 1));
+ }
+
+ public static double[] calcAxis(double[] v1, double[] v2) {
+ double[] vec1 = normal(v1);
+ double[] vec2 = normal(v2);
+ return normal(cross(vec1, vec2));
+ }
+
+ public void set(double angle, double[] axis) {
+ x[0] = Math.cos(angle / 2);
+ double sin = Math.sin(angle / 2);
+ x[1] = axis[0] * sin;
+ x[2] = axis[1] * sin;
+ x[3] = axis[2] * sin;
+ }
+
+ public Quaternion(double x0, double x1, double x2, double x3) {
+ x[0] = x0;
+ x[1] = x1;
+ x[2] = x2;
+ x[3] = x3;
+ }
+
+ public Quaternion conjugate() {
+ return new Quaternion(x[0], -x[1], -x[2], -x[3]);
+ }
+
+ public Quaternion plus(Quaternion b) {
+ Quaternion a = this;
+ return new Quaternion(a.x[0] + b.x[0], a.x[1] + b.x[1], a.x[2] + b.x[2], a.x[3] + b.x[3]);
+ }
+
+ public Quaternion times(Quaternion b) {
+ Quaternion a = this;
+ double y0 = a.x[0] * b.x[0] - a.x[1] * b.x[1] - a.x[2] * b.x[2] - a.x[3] * b.x[3];
+ double y1 = a.x[0] * b.x[1] + a.x[1] * b.x[0] + a.x[2] * b.x[3] - a.x[3] * b.x[2];
+ double y2 = a.x[0] * b.x[2] - a.x[1] * b.x[3] + a.x[2] * b.x[0] + a.x[3] * b.x[1];
+ double y3 = a.x[0] * b.x[3] + a.x[1] * b.x[2] - a.x[2] * b.x[1] + a.x[3] * b.x[0];
+ return new Quaternion(y0, y1, y2, y3);
+ }
+
+ public Quaternion inverse() {
+ double d = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];
+ return new Quaternion(x[0] / d, -x[1] / d, -x[2] / d, -x[3] / d);
+ }
+
+ public Quaternion divides(Quaternion b) {
+ Quaternion a = this;
+ return a.inverse().times(b);
+ }
+
+
+ public double[] rotateVec(double[] v) {
+
+ double v0 = v[0];
+ double v1 = v[1];
+ double v2 = v[2];
+
+ double s = x[1] * v0 + x[2] * v1 + x[3] * v2;
+
+ double n0 = 2 * (x[0] * (v0 * x[0] - (x[2] * v2 - x[3] * v1)) + s * x[1]) - v0;
+ double n1 = 2 * (x[0] * (v1 * x[0] - (x[3] * v0 - x[1] * v2)) + s * x[2]) - v1;
+ double n2 = 2 * (x[0] * (v2 * x[0] - (x[1] * v1 - x[2] * v0)) + s * x[3]) - v2;
+
+ return new double[]{n0, n1, n2};
+
+ }
+
+ void matrix() {
+ double xx = x[1] * x[1];
+ double xy = x[1] * x[2];
+ double xz = x[1] * x[3];
+ double xw = x[1] * x[0];
+
+ double yy = x[2] * x[2];
+ double yz = x[2] * x[3];
+ double yw = x[2] * x[0];
+
+ double zz = x[3] * x[3];
+ double zw = x[3] * x[0];
+ double[] m = new double[16];
+ m[0] = 1 - 2 * (yy + zz);
+ m[1] = 2 * (xy - zw);
+ m[2] = 2 * (xz + yw);
+
+ m[4] = 2 * (xy + zw);
+ m[5] = 1 - 2 * (xx + zz);
+ m[6] = 2 * (yz - xw);
+
+ m[8] = 2 * (xz - yw);
+ m[9] = 2 * (yz + xw);
+ m[10] = 1 - 2 * (xx + yy);
+
+ m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0;
+ m[15] = 1;
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java
new file mode 100644
index 00000000..a9495ce4
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+/**
+ * create bricked binary representation of the non transparent voxels
+ */
+public class RsBrickedBitMask {
+ private static final String LOGTAG = "BrickedBitMask";
+ ScriptC_bricked scriptC_bricked;
+
+ int mDimX;
+ int mDimY;
+ int mDimZ;
+ int m_bricks_dimx;
+ int m_bricks_dimy;
+ int m_bricks_dimz;
+ Volume mVolume;
+ int mBrickCnt = 0;
+
+ Allocation mBrick_allocation;
+
+ public final static int BSIZE = 32;
+
+ public static final byte TYPE_BYTE = 1;
+ public static final byte TYPE_SHORT = 1;
+ public static final byte TYPE_INT = 2;
+
+ public RsBrickedBitMask(VrState state) {
+
+ mVolume = state.mVolume;
+ mDimX = mVolume.mDimx;
+ mDimY = mVolume.mDimy;
+ mDimZ = mVolume.mDimz;
+ m_bricks_dimx = (mDimX + 31) / 32;
+ m_bricks_dimy = (mDimY + 31) / 32;
+ m_bricks_dimz = (mDimZ + 31) / 32;
+ int maxBrick = m_bricks_dimx * m_bricks_dimy * m_bricks_dimz;
+ int size = maxBrick * 32 * 32; // divide by 4 because we will try U32_4
+
+ Type.Builder b = new Type.Builder(state.mRs, android.renderscript.Element.U32(state.mRs));
+ b.setX(size);
+ mBrick_allocation = Allocation.createTyped(state.mRs, b.create(), Allocation.USAGE_SCRIPT);
+
+ scriptC_bricked = new ScriptC_bricked(state.mRs);
+
+ scriptC_bricked.set_volume(mVolume.mVolumeAllocation);
+ scriptC_bricked.set_brick_dimx(m_bricks_dimx);
+ scriptC_bricked.set_brick_dimy(m_bricks_dimy);
+ scriptC_bricked.set_brick_dimz(m_bricks_dimz);
+ scriptC_bricked.set_opacity(state.mMaterial.getOpacityAllocation(state.mRs));
+ state.mRs.finish();
+
+ scriptC_bricked.forEach_pack_chunk(mBrick_allocation);
+
+ Allocation tmp = Allocation.createTyped(state.mRs, b.create(), Allocation.USAGE_SCRIPT);
+ scriptC_bricked.set_bricks(mBrick_allocation);
+ scriptC_bricked.forEach_dilate(mBrick_allocation, tmp);
+
+ mBrick_allocation.destroy();
+ mBrick_allocation = tmp;
+ }
+
+ Allocation createChunkAllocation(RenderScript rs) {
+ Type.Builder b = new Type.Builder(rs, android.renderscript.Element.U32(rs));
+ b.setX(mDimX / 8);
+ b.setY(mDimY);
+ b.setZ(32);
+ return Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+ }
+
+ Allocation createBitChunkAllocation(RenderScript rs) {
+ Type.Builder b = new Type.Builder(rs, android.renderscript.Element.U8(rs));
+ b.setX(mDimX / 8);
+ b.setY(mDimY);
+ b.setZ(32);
+ return Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java
new file mode 100644
index 00000000..5dc5fc3d
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+import java.text.DecimalFormat;
+
+/**
+ * code to manage transformations between world screen and volume space.
+ */
+public class Transform {
+ public final static char WORLD_SPACE = 0;
+ public final static char SCREEN_SPACE = 1;
+ public final static char VOLUME_SPACE = 2;
+ private static final String LOGTAG = "Transform";
+
+ Matrix[][] mAllMat = new Matrix[3][3];
+ public ViewMatrix mViewMatrix = new ViewMatrix();
+ float[] mVoxelDim = new float[3];
+
+ public void clone(Transform src) {
+ System.arraycopy(src.mVoxelDim, 0, mVoxelDim, 0, mVoxelDim.length);
+ mViewMatrix.clone(src.mViewMatrix);
+ updateAllMatrix();
+ }
+
+ public void updateAllMatrix() {
+ mViewMatrix.calcMatrix();
+ Matrix m = new Matrix();
+ m.setToUnit();
+ mAllMat[0][0] = m;
+ mAllMat[1][1] = m;
+ mAllMat[2][2] = m;
+ mAllMat[SCREEN_SPACE][WORLD_SPACE] = new Matrix(mViewMatrix);
+ mAllMat[WORLD_SPACE][SCREEN_SPACE] = mViewMatrix.invers();
+ m = new Matrix();
+ m.setToUnit();
+ if (mVoxelDim[0] > 0) {
+ int min = 0;
+
+ m.m[0] = 1 / mVoxelDim[0];
+ m.m[5] = 1 / mVoxelDim[1];
+ m.m[10] = 1 / mVoxelDim[2];
+ }
+ mAllMat[WORLD_SPACE][VOLUME_SPACE] = m;
+ mAllMat[VOLUME_SPACE][WORLD_SPACE] = m.invers();
+ mAllMat[SCREEN_SPACE][VOLUME_SPACE] = m = m.premult(mViewMatrix);
+ mAllMat[VOLUME_SPACE][SCREEN_SPACE] = m.invers();
+
+ }
+
+ public void setVoxelDim(float[] volDim) {
+ mVoxelDim[0] = volDim[0];
+ mVoxelDim[1] = volDim[1];
+ mVoxelDim[2] = volDim[2];
+ }
+
+ public Matrix getMatrix(char from, char to) {
+ return mAllMat[from][to];
+ }
+
+ public void setScreenDim(int x, int y) {
+ mViewMatrix.setScreenDim(x, y);
+ updateAllMatrix();
+ }
+
+ public double[] getLookPoint() {
+ return mViewMatrix.getLookPoint();
+ }
+
+ public void setLookPoint(double[] mLookPoint) {
+ mViewMatrix.setLookPoint(mLookPoint);
+ updateAllMatrix();
+ }
+
+ public double[] getEyePoint() {
+ return mViewMatrix.getEyePoint();
+ }
+
+ public void setEyePoint(double[] mEyePoint) {
+ mViewMatrix.setEyePoint(mEyePoint);
+ updateAllMatrix();
+ }
+
+ public double[] getUpVector() {
+ return mViewMatrix.getUpVector();
+ }
+
+ public void setUpVector(double[] mUpVector) {
+ mViewMatrix.setUpVector(mUpVector);
+ updateAllMatrix();
+ }
+
+ public double getScreenWidth() {
+ return mViewMatrix.getScreenWidth();
+ }
+
+ public void setScreenWidth(double screenWidth) {
+ mViewMatrix.setScreenWidth(screenWidth);
+ updateAllMatrix();
+ }
+
+ public void lookAt(TriData tri, int w, int h) {
+ mViewMatrix.lookAt(tri, mVoxelDim, w, h);
+ updateAllMatrix();
+ }
+
+ public void look(char dir, TriData tri, int w, int h) {
+ mViewMatrix.look(dir, tri, mVoxelDim, w, h);
+ updateAllMatrix();
+ }
+
+ public void trackBallUp(float x, float y) {
+ mViewMatrix.trackBallUP(x, y);
+ updateAllMatrix();
+ }
+
+ public void trackBallDown(float x, float y) {
+ mViewMatrix.trackBallDown(x, y);
+ }
+
+ public void trackBallMove(float x, float y) {
+ mViewMatrix.trackBallMove(x, y);
+ updateAllMatrix();
+ }
+
+ static DecimalFormat df = new DecimalFormat(" ##0.000");
+
+ private static String trim(double d) {
+ String s = df.format(d);
+ return s.substring(s.length() - 6);
+ }
+
+ public static void print(float[] d) {
+ String s = "";
+ for (int i = 0; i < d.length; i++) {
+ s += (((i == 0) ? "[ " : " , ") + trim(d[i]));
+ }
+ Log.v(LOGTAG, s + "]");
+ }
+
+ public static void main(String[] args) {
+ int[] voldim = {50, 50, 100};
+ double[] mEyePoint = {voldim[0] / 2., -voldim[1] / 2., voldim[2] / 2.};
+ double[] mLookPoint = {voldim[0] / 2., voldim[1] / 2., voldim[2] / 2.};
+ double[] mUpVector = {0., 0., 1.};
+
+ Transform t = new Transform();
+ t.mVoxelDim[0] = 1;
+ t.setEyePoint(mEyePoint);
+ t.setLookPoint(mLookPoint);
+ t.setUpVector(mUpVector);
+ t.setScreenDim(256, 256);
+ t.setScreenWidth(128);
+ t.updateAllMatrix();
+
+ Matrix m = t.getMatrix(SCREEN_SPACE, VOLUME_SPACE);
+ float[] orig = {.5f, .5f, 0};
+ float[] ret = new float[3];
+
+ m.mult3(orig, ret);
+ print(ret);
+ float[] look = {0, 0, 1};
+ m.mult3v(look, ret);
+ print(ret);
+ float[] up = {1, 0, 0};
+ m.mult3v(up, ret);
+ print(ret);
+ float[] right = {0, 1, 0};
+ m.mult3v(right, ret);
+ print(ret);
+
+ }
+
+ public void print() {
+ Log.v(LOGTAG, "==== =========== VIEW ========== ======");
+
+ mViewMatrix.print();
+ Log.v(LOGTAG, "==== SCREEN_SPACE to WORLD_SPACE ======");
+ mAllMat[SCREEN_SPACE][WORLD_SPACE].print();
+ Log.v(LOGTAG, "==== SCREEN_SPACE to VOLUME_SPACE ======");
+ mAllMat[SCREEN_SPACE][VOLUME_SPACE].print();
+ Log.v(LOGTAG, "==== WORLD_SPACE to VOLUME_SPACE ======");
+ mAllMat[WORLD_SPACE][VOLUME_SPACE].print();
+ Log.v(LOGTAG, "==== WORLD_SPACE to SCREEN_SPACE ======");
+ mAllMat[WORLD_SPACE][SCREEN_SPACE].print();
+ Log.v(LOGTAG, "==== VOLUME_SPACE to SCREEN_SPACE ======");
+ mAllMat[VOLUME_SPACE][SCREEN_SPACE].print();
+ Log.v(LOGTAG, "==== VOLUME_SPACE to WORLD_SPACE ======");
+ mAllMat[VOLUME_SPACE][WORLD_SPACE].print();
+ Log.v(LOGTAG, "=======================================");
+ }
+} \ No newline at end of file
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java
new file mode 100644
index 00000000..316475a9
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+
+/**
+ * Simple representation triangulated surface
+ */
+public class TriData {
+ private static final String LOGTAG = "TriData";
+ protected float[] mVert;
+ protected int[] mIndex;
+
+ public TriData() {
+ }
+
+ public void print() {
+ class Fmt extends DecimalFormat {
+ public Fmt() {
+ super(" ##0.000");
+ }
+
+ public String f(double number) {
+ String ret = " "+super.format(number);
+ return ret.substring(ret.length() - 7);
+ }
+ }
+ Fmt df = new Fmt();
+ for (int i = 0; i < mVert.length; i += 3) {
+
+ String s = (i / 3 + "[ " + df.f(mVert[i]));
+ s += (", " + df.f(mVert[i + 1]));
+ Log.v(LOGTAG, s + ", " + df.f(mVert[i + 2]) + "]");
+ }
+ }
+
+ public TriData(TriData clone) {
+
+ mVert = Arrays.copyOf(clone.mVert, clone.mVert.length);
+ mIndex = Arrays.copyOf(clone.mIndex, clone.mIndex.length);
+ }
+
+ public void scale(float[] s) {
+ for (int i = 0; i < mVert.length; i += 3) {
+ mVert[i] *= s[0];
+ mVert[i + 1] *= s[1];
+ mVert[i + 2] *= s[2];
+ }
+ }
+
+ public void scale(double[] s) {
+ for (int i = 0; i < mVert.length; i += 3) {
+ mVert[i] *= s[0];
+ mVert[i + 1] *= s[1];
+ mVert[i + 2] *= s[2];
+ }
+ }
+
+ public void transform(Matrix m) {
+ for (int i = 0; i < mVert.length; i += 3) {
+ m.mult3(mVert, i, mVert, i);
+ }
+ }
+
+ public void transform(Matrix m, TriData out) {
+
+ for (int i = 0; i < mVert.length; i += 3) {
+ m.mult3(mVert, i, out.mVert, i);
+ }
+ }
+
+ /**
+ * Read some simple triangle format used in testing
+ * @param fileName
+ */
+ public void read(String fileName) {
+ try {
+ FileReader fr = new FileReader(fileName);
+ LineNumberReader lnr = new LineNumberReader(fr);
+ int num_verts = Integer.parseInt(lnr.readLine());
+ Log.v(LOGTAG, "verts =" + num_verts);
+ mVert = new float[num_verts * 3];
+ int k = 0;
+ for (int i = 0; i < num_verts; i++) {
+ String[] s = lnr.readLine().split("\\s");
+
+ for (int j = 0; j < s.length; j++) {
+ mVert[k++] = Float.parseFloat(s[j]);
+ }
+ }
+ int num_tri = Integer.parseInt(lnr.readLine());
+ Log.v(LOGTAG, "tri =" + num_tri);
+ mIndex = new int[3 * num_tri];
+ k = 0;
+ for (int i = 0; i < num_tri; i++) {
+ String[] s = lnr.readLine().split("\\s");
+ for (int j = 0; j < s.length; j++) {
+ mIndex[k++] = Integer.parseInt(s[j]);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java
new file mode 100644
index 00000000..973d705f
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+/**
+ * Some minimal utilities for Vector math
+ */
+public class VectorUtil {
+ public static void sub(double[] a, double[] b, double[] out) {
+ out[0] = a[0] - b[0];
+ out[1] = a[1] - b[1];
+ out[2] = a[2] - b[2];
+ }
+
+ public static void mult(double[] a, double b, double[] out) {
+ out[0] = a[0] * b;
+ out[1] = a[1] * b;
+ out[2] = a[2] * b;
+ }
+
+ public static double dot(double[] a, double[] b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+ }
+
+ public static double norm(double[] a) {
+ return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
+ }
+
+ public static void cross(double[] a, double[] b, double[] out) {
+ double out0 = a[1] * b[2] - b[1] * a[2];
+ double out1 = a[2] * b[0] - b[2] * a[0];
+ double out2 = a[0] * b[1] - b[0] * a[1];
+ out[0] = out0;
+ out[1] = out1;
+ out[2] = out2;
+ }
+
+ public static void normalize(double[] a) {
+ double norm = norm(a);
+ a[0] /= norm;
+ a[1] /= norm;
+ a[2] /= norm;
+ }
+
+ public static void add(double[] a, double[] b,
+ double[] out) {
+ out[0] = a[0] + b[0];
+ out[1] = a[1] + b[1];
+ out[2] = a[2] + b[2];
+ }
+
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java
new file mode 100644
index 00000000..b7c172a4
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+import java.text.DecimalFormat;
+import java.util.Arrays;
+
+public class ViewMatrix extends Matrix {
+ private static final String LOGTAG = "ViewMatrix";
+ private double[] mLookPoint;
+ private double[] mEyePoint;
+ private double[] mUpVector;
+ private double mScreenWidth;
+ private int[] mScreenDim;
+
+ private Matrix mStartMatrix;
+ private double[] mStartV = new double[3];
+ private double[] mMoveToV = new double[3];
+ private double[] mStartEyePoint;
+ private double[] mStartUpVector;
+ private Quaternion mQ = new Quaternion(0, 0, 0, 0);
+
+ public final static char UP_AT = 0x001;
+ public final static char DOWN_AT = 0x002;
+ public final static char RIGHT_AT = 0x010;
+ public final static char LEFT_AT = 0x020;
+ public final static char FORWARD_AT = 0x100;
+ public final static char BEHIND_AT = 0x200;
+
+ public void clone(ViewMatrix src) {
+ if (src.mLookPoint != null) {
+ System.arraycopy(src.mLookPoint, 0, mLookPoint, 0, mLookPoint.length);
+ }
+ if (src.mEyePoint != null) {
+ System.arraycopy(src.mEyePoint, 0, mEyePoint, 0, mEyePoint.length);
+ }
+ if (src.mUpVector != null) {
+ System.arraycopy(src.mUpVector, 0, mUpVector, 0, mUpVector.length);
+ }
+ mScreenWidth = src.mScreenWidth;
+
+ if (src.mScreenDim != null) {
+ System.arraycopy(src.mScreenDim, 0, mScreenDim, 0, mScreenDim.length);
+ }
+ if (src.mStartV != null) {
+ System.arraycopy(src.mStartV, 0, mStartV, 0, mStartV.length);
+ }
+ if (src.mMoveToV != null) {
+ System.arraycopy(src.mMoveToV, 0, mMoveToV, 0, mMoveToV.length);
+ }
+
+
+ if (src.mStartEyePoint != null) {
+ if (mStartEyePoint == null) {
+ mStartEyePoint = Arrays.copyOf(src.mStartEyePoint,src.mStartEyePoint.length);
+ } else {
+ System.arraycopy(src.mStartEyePoint, 0, mStartEyePoint, 0, mStartEyePoint.length);
+ }
+ }
+ if (src.mStartUpVector != null) {
+ if (mStartUpVector == null) {
+ mStartUpVector = Arrays.copyOf(src.mStartUpVector,src.mStartUpVector.length);
+ } else {
+ System.arraycopy(src.mStartUpVector, 0, mStartUpVector, 0, mStartUpVector.length);
+ }
+ }
+ if (src.mStartMatrix != null) {
+ if (mStartMatrix == null) {
+ mStartMatrix = new Matrix();
+ }
+ mStartMatrix.clone(src.mStartMatrix);
+ }
+ mQ.clone(src.mQ);
+ super.clone(src);
+ }
+
+ private static String toStr(double d) {
+ String s = " " + df.format(d);
+ return s.substring(s.length() - 9);
+ }
+
+ private static String toStr(double[] d) {
+ String s = "[";
+ for (int i = 0; i < d.length; i++) {
+ s += toStr(d[i]);
+ }
+
+ return s + "]";
+ }
+
+ private static DecimalFormat df = new DecimalFormat("##0.000");
+
+ @Override
+ public void print() {
+ Log.v(LOGTAG, "mLookPoint :" + toStr(mLookPoint));
+ Log.v(LOGTAG, "mEyePoint :" + toStr(mEyePoint));
+ Log.v(LOGTAG, "mUpVector :" + toStr(mUpVector));
+ Log.v(LOGTAG, "mScreenWidth: " + toStr(mScreenWidth));
+ Log.v(LOGTAG, "mScreenDim :[" + mScreenDim[0] + ", " + mScreenDim[1] + "]");
+ }
+
+ public ViewMatrix() {
+ mLookPoint = new double[3];
+ mEyePoint = new double[3];
+ mUpVector = new double[3];
+ mScreenDim = new int[2];
+ }
+
+ public void setScreenDim(int x, int y) {
+ mScreenDim = new int[]{x, y};
+ }
+
+ public double[] getLookPoint() {
+ return mLookPoint;
+ }
+
+ public void setLookPoint(double[] mLookPoint) {
+ this.mLookPoint = mLookPoint;
+ }
+
+ public double[] getEyePoint() {
+ return mEyePoint;
+ }
+
+ public void setEyePoint(double[] mEyePoint) {
+ this.mEyePoint = mEyePoint;
+ }
+
+ public double[] getUpVector() {
+ return mUpVector;
+ }
+
+ public void setUpVector(double[] mUpVector) {
+ this.mUpVector = mUpVector;
+ }
+
+ public double getScreenWidth() {
+ return mScreenWidth;
+ }
+
+ public void setScreenWidth(double screenWidth) {
+ this.mScreenWidth = screenWidth;
+ }
+
+ public void makeUnit() {
+
+ }
+
+ public void calcMatrix() {
+ if (mScreenDim == null) {
+ return;
+ }
+ double scale = mScreenWidth / mScreenDim[0];
+ double[] zv = {
+ mLookPoint[0] - mEyePoint[0],
+ mLookPoint[1] - mEyePoint[1],
+ mLookPoint[2] - mEyePoint[2]
+ };
+ VectorUtil.normalize(zv);
+
+
+ double[] m = new double[16];
+ m[2] = zv[0];
+ m[6] = zv[1];
+ m[10] = zv[2];
+ m[14] = 0;
+
+ calcRight(zv, mUpVector, zv);
+ double[] right = zv;
+
+ m[0] = right[0] * scale;
+ m[4] = right[1] * scale;
+ m[8] = right[2] * scale;
+ m[12] = 0;
+
+ m[1] = -mUpVector[0] * scale;
+ m[5] = -mUpVector[1] * scale;
+ m[9] = -mUpVector[2] * scale;
+ m[13] = 0;
+ double sw = mScreenDim[0] / 2 - 0.5;
+ double sh = mScreenDim[1] / 2 - 0.5;
+ double sz = -0.5;
+ m[3] = mEyePoint[0] - (m[0] * sw + m[1] * sh + m[2] * sz);
+ m[7] = mEyePoint[1] - (m[4] * sw + m[5] * sh + m[6] * sz);
+ m[11] = mEyePoint[2] - (m[8] * sw + m[9] * sh + m[10] * sz);
+
+ m[15] = 1;
+ this.m = m;
+ }
+
+ static void calcRight(double[] a, double[] b, double[] out) {
+ VectorUtil.cross(a, b, out);
+ }
+
+ public static void main(String[] args) {
+ double[] up = {0, 0, 1};
+ double[] look = {0, 0, 0};
+ double[] eye = {-10, 0, 0};
+ ViewMatrix v = new ViewMatrix();
+ v.setEyePoint(eye);
+ v.setLookPoint(look);
+ v.setUpVector(up);
+ v.setScreenWidth(10);
+ v.setScreenDim(512, 512);
+ v.calcMatrix();
+ }
+
+ private void calcLook(TriData tri, float[] voxelDim, int w, int h) {
+ float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
+ float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
+
+ for (int i = 0; i < tri.mVert.length; i += 3) {
+ maxx = Math.max(tri.mVert[i], maxx);
+ minx = Math.min(tri.mVert[i], minx);
+ maxy = Math.max(tri.mVert[i + 1], maxy);
+ miny = Math.min(tri.mVert[i + 1], miny);
+ maxz = Math.max(tri.mVert[i + 2], maxz);
+ minz = Math.min(tri.mVert[i + 2], minz);
+ }
+ mLookPoint = new double[]{voxelDim[0] * (maxx + minx) / 2,
+ voxelDim[1] * (maxy + miny) / 2,
+ voxelDim[2] * (maxz + minz) / 2};
+ mScreenWidth = Math.max(voxelDim[0] * (maxx - minx),
+ Math.max(voxelDim[1] * (maxy - miny),
+ voxelDim[2] * (maxz - minz))) * 2;
+ }
+
+ private void calcLook(TriData triW, int w, int h) {
+ float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
+ float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
+
+ for (int i = 0; i < triW.mVert.length; i += 3) {
+ maxx = Math.max(triW.mVert[i], maxx);
+ minx = Math.min(triW.mVert[i], minx);
+ maxy = Math.max(triW.mVert[i + 1], maxy);
+ miny = Math.min(triW.mVert[i + 1], miny);
+ maxz = Math.max(triW.mVert[i + 2], maxz);
+ minz = Math.min(triW.mVert[i + 2], minz);
+ }
+ mLookPoint = new double[]{(maxx + minx) / 2, (maxy + miny) / 2, (maxz + minz) / 2};
+
+ mScreenWidth = 2 * Math.max((maxx - minx), Math.max((maxy - miny), (maxz - minz)));
+ }
+
+ public void look(char dir, TriData tri, float[] voxelDim, int w, int h) {
+ calcLook(tri, w, h);
+ int dx = ((dir >> 4) & 0xF);
+ int dy = ((dir >> 8) & 0xF);
+ int dz = ((dir >> 0) & 0xF);
+ if (dx > 1) {
+ dx = -1;
+ }
+ if (dy > 1) {
+ dy = -1;
+ }
+ if (dz > 1) {
+ dz = -1;
+ }
+ mEyePoint = new double[]{mLookPoint[0] + 2 * mScreenWidth * dx,
+ mLookPoint[1] + 2 * mScreenWidth * dy,
+ mLookPoint[2] + 2 * mScreenWidth * dz};
+ double[] zv = new double[]{-dx, -dy, -dz};
+ double[] rv = new double[]{(dx == 0) ? 1 : 0, (dx == 0) ? 0 : 1, 0};
+ double[] up = new double[3];
+ VectorUtil.norm(zv);
+ VectorUtil.norm(rv);
+
+ VectorUtil.cross(zv, rv, up);
+ VectorUtil.cross(zv, up, rv);
+ VectorUtil.cross(zv, rv, up);
+ mUpVector = up;
+ mScreenDim = new int[]{w, h};
+ calcMatrix();
+ }
+
+ public void lookAt(TriData tri, float[] voxelDim, int w, int h) {
+ calcLook(tri, voxelDim, w, h);
+
+ mEyePoint = new double[]{mLookPoint[0] + mScreenWidth,
+ mLookPoint[1] + mScreenWidth,
+ mLookPoint[2] + mScreenWidth};
+ double[] zv = new double[]{-1, -1, -1};
+ double[] rv = new double[]{1, 1, 0};
+ double[] up = new double[3];
+ VectorUtil.norm(zv);
+ VectorUtil.norm(rv);
+
+ VectorUtil.cross(zv, rv, up);
+ VectorUtil.cross(zv, up, rv);
+ VectorUtil.cross(zv, rv, up);
+ mUpVector = up;
+ mScreenDim = new int[]{w, h};
+ calcMatrix();
+ }
+
+ public void trackBallUP(float x, float y) {
+
+ }
+
+ public void trackBallDown(float x, float y) {
+
+ ballToVec(x, y, mStartV);
+ mStartEyePoint = Arrays.copyOf(mEyePoint, m.length);
+ mStartUpVector = Arrays.copyOf(mUpVector, m.length);
+ mStartMatrix = new Matrix(this);
+ mStartMatrix.makeRotation();
+ }
+
+ public void trackBallMove(float x, float y) {
+ ballToVec(x, y, mMoveToV);
+
+ double angle = Quaternion.calcAngle(mStartV, mMoveToV);
+ if (angle < 0.0001) {
+ calcMatrix();
+ return;
+ }
+ double[] axis = Quaternion.calcAxis(mStartV, mMoveToV);
+
+
+ axis = mStartMatrix.vecmult(axis);
+
+ mQ.set(angle, axis);
+
+ VectorUtil.sub(mLookPoint, mStartEyePoint, mEyePoint);
+
+ mEyePoint = mQ.rotateVec(mEyePoint);
+ mUpVector = mQ.rotateVec(mStartUpVector);
+
+ VectorUtil.sub(mLookPoint, mEyePoint, mEyePoint);
+ calcMatrix();
+ }
+
+ void ballToVec(float x, float y, double[] v) {
+ float ballRadius = Math.min(mScreenDim[0], mScreenDim[1]) * .4f;
+ double cx = mScreenDim[0] / 2.;
+ double cy = mScreenDim[1] / 2.;
+
+ double dx = (cx - x) / ballRadius;
+ double dy = (cy - y) / ballRadius;
+ double scale = dx * dx + dy * dy;
+ if (scale > 1) {
+ scale = Math.sqrt(scale);
+ dx = dx / scale;
+ dy = dy / scale;
+ }
+
+ double dz = Math.sqrt(Math.abs(1 - (dx * dx + dy * dy)));
+ v[0] = dx;
+ v[1] = dy;
+ v[2] = dz;
+ VectorUtil.normalize(v);
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java
new file mode 100644
index 00000000..0de55447
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.renderscript.Allocation;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * Defines a simple volume to be used in the volume renderer
+ */
+public class Volume {
+ private static final String LOGTAG = "Volume";
+ public short[][] mData;
+ public Allocation mVolumeAllocation; // one big volume
+ public int mDimz = -1;
+ public int mDimy = -1;
+ public int mDimx = -1;
+ public float[] mVoxelDim = new float[]{1f, 1f, 1f};
+ private HashMap<String, Look> mLooks = new HashMap<String, Look>();
+
+ @Override
+ public String toString() {
+ String ret = "Volume[" + mDimx + "," + mDimy + "," + mDimz + "]";
+ ret += "(" + mVoxelDim[0] + ", " + mVoxelDim[1] + ", " + mVoxelDim[2] + ")";
+
+ return ret;
+ }
+
+ public String[] getLookNames() {
+ return mLooks.keySet().toArray(new String[mLooks.size()]);
+ }
+
+ public int[][] getLookColor(String name) {
+ return mLooks.get(name).mColor;
+ }
+
+ public int[][] getLookOpactiy(String name) {
+ return mLooks.get(name).mOpacity;
+ }
+
+ public void addLook(String name, int[][] color, int[][] opacity) {
+ mLooks.put(name, new Look(name, color, opacity));
+ }
+
+ public void addLook(String name, String color_string, String opacity_string) {
+ mLooks.put(name, new Look(name, color_string, opacity_string));
+ Look l = mLooks.get(name);
+ Log.v(LOGTAG, " ========================== " + name + " =============================");
+ Log.v(LOGTAG, "mColor "+l.dblArrayToString(l.mColor));
+ Log.v(LOGTAG, "mOpacity "+l.dblArrayToString(l.mOpacity));
+ }
+
+ class Look {
+ int[][] mColor;
+ int[][] mOpacity;
+ String mName;
+
+ public Look(String name, String color_string, String opacity_string) {
+ mName = name;
+ String[] colorSplit = color_string.split("\\}\\s*\\,\\s*\\{");
+ String[] opacitySplit = opacity_string.split("\\}\\s*\\,\\s*\\{");
+ mColor = new int[colorSplit.length][];
+ for (int i = 0; i < colorSplit.length; i++) {
+
+ mColor[i] = readNumbers(colorSplit[i]);
+ }
+ mOpacity = new int[opacitySplit.length][];
+ for (int i = 0; i < opacitySplit.length; i++) {
+
+ mOpacity[i] = readNumbers(opacitySplit[i]);
+ }
+ }
+
+ public Look(String name, int[][] color, int[][] opacity) {
+ mColor = color;
+ mOpacity = opacity;
+ mName =name;
+ }
+
+ private int[] readNumbers(String numList) {
+ numList = numList.replace('{', ' ');
+ numList = numList.replace('}', ' ');
+ numList = numList.replace(';', ' ');
+ String[] split = numList.split(",");
+ int[] ret = new int[split.length];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = Integer.decode(split[i].trim());
+ }
+ return ret;
+ }
+
+ private String dblArrayToString(int[][] v) {
+ String s = "";
+ for (int i = 0; i < v.length; i++) {
+ if (i > 0) {
+ s += ",";
+ }
+ s += Arrays.toString(v[i]);
+ }
+ return s;
+ }
+
+ public String toString() {
+ return "mColor=" + dblArrayToString(mColor) + "\nmOpacity=" + dblArrayToString(mOpacity);
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java
new file mode 100644
index 00000000..291a5880
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.renderscript.Matrix3f;
+import android.renderscript.Matrix4f;
+import android.renderscript.Script;
+import android.renderscript.ScriptIntrinsicResize;
+import android.util.Log;
+
+import java.text.DecimalFormat;
+
+public class VrPipline1 extends BasicPipeline {
+ private static final String LOGTAG = "VrPipline1";
+
+ float[] mMatrixBuffer = new float[16];
+ ScriptC_vr scriptC_vr;
+ ScriptIntrinsicResize script_resize;
+ Script.LaunchOptions options = new Script.LaunchOptions();
+
+ @Override
+ public void initBuffers(VrState state) {
+ super.initBuffers(state);
+ }
+
+ static DecimalFormat df = new DecimalFormat(" ##0.000");
+
+ private static String trim(double d) {
+ String s = df.format(d);
+ return s.substring(s.length() - 6);
+ }
+
+ private static String trim(float[] d) {
+ String ret = "";
+ for (int i = 0; i < d.length; i++) {
+ ret += ((i == 0) ? "[ " : " , ") + trim(d[i]);
+ }
+ return ret + ("]");
+ }
+
+ private void creatOpacityAllocation(VrState state) {
+ scriptC_vr.set_opacity(state.mMaterial.getOpacityAllocation(state.mRs));
+ }
+
+ private void creatColorMapAllocation(VrState state) {
+ scriptC_vr.set_color_map(state.mMaterial.getColorMapAllocation(state.mRs));
+ }
+
+ @Override
+ public void setupTriangles(VrState state) {
+ super.setupTriangles(state);
+ if (mCancel){
+ return;
+ }
+ Matrix m = state.mTransform.getMatrix(Transform.SCREEN_SPACE, Transform.VOLUME_SPACE);
+ m.getAsFloats(mMatrixBuffer);
+ Matrix4f matrix4f = new Matrix4f(mMatrixBuffer);
+ if (scriptC_vr == null) {
+ scriptC_vr = new ScriptC_vr(state.mRs);
+ }
+ if (script_resize == null) {
+ script_resize = ScriptIntrinsicResize.create(state.mRs);
+ }
+ scriptC_vr.set_matrix4(matrix4f);
+ for (int i = 0; i < 9; i++) {
+ int x = i % 3;
+ int y = i / 3;
+ mMatrixBuffer[i] = mMatrixBuffer[x + y * 4];
+ }
+ Matrix3f matrix3f = new Matrix3f(mMatrixBuffer);
+ scriptC_vr.set_matrix3(matrix3f);
+ creatColorMapAllocation(state);
+ creatOpacityAllocation(state);
+ scriptC_vr.invoke_setup_vectors();
+ }
+
+ @Override
+ public void raycast(VrState state) {
+ if (mCancel){
+ return;
+ }
+ scriptC_vr.set_volume(state.mVolume.mVolumeAllocation);
+ scriptC_vr.set_bricks(state.mRsMask.mBrick_allocation);
+ scriptC_vr.set_brick_dimx(state.mRsMask.m_bricks_dimx);
+ scriptC_vr.set_brick_dimy(state.mRsMask.m_bricks_dimy);
+ if (mCancel){
+ return;
+ }
+ scriptC_vr.set_zbuff(state.mzRangeFullAllocation);
+ if (mCancel){
+ return;
+ }
+ if (state.mImgWidth*state.mImgHeight < 512*512) {
+ scriptC_vr.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation);
+ } else {
+ int blocks = state.mImgWidth*state.mImgHeight/(256*256);
+ for (int i = 0; i < blocks; i++) {
+ options.setX(0,state.mImgWidth);
+ options.setY(i*state.mImgHeight/blocks, (i+1)*state.mImgHeight/blocks);
+ scriptC_vr.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation, options);
+ state.mRs.finish();
+ if (mCancel){
+ Log.v(LOGTAG, "cancel");
+ return;
+ }
+ }
+
+
+ }
+
+ }
+
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java
new file mode 100644
index 00000000..1ac57383
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.graphics.SurfaceTexture;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+import android.view.Surface;
+
+public class VrState {
+ private static final String LOGTAG = "VrState";
+ public RenderScript mRs;
+ public Volume mVolume;
+ public RsBrickedBitMask mRsMask;
+ public Material mMaterial = new Material();
+ public Cube mCubeVolume;
+ public TriData mCubeScreen;
+ public int mImgWidth;
+ public int mImgHeight;
+ Allocation mzRangeFullAllocation;
+ public Allocation mScrAllocation; // the RGB data out
+ public Transform mTransform = new Transform();
+
+ public void clone(VrState src) {
+ mRs = src.mRs;
+ mVolume = src.mVolume;
+ mRsMask = src.mRsMask;
+ mMaterial = src.mMaterial;
+ if (mCubeVolume == null) {
+ mCubeVolume = new Cube();
+ }
+ mCubeVolume.clone(src.mCubeVolume);
+ mCubeScreen = new TriData(src.mCubeScreen);
+ mImgWidth = src.mImgWidth;
+ mImgHeight = src.mImgHeight;
+ mzRangeFullAllocation = src.mzRangeFullAllocation;
+ mScrAllocation = src.mScrAllocation;
+ mTransform.clone(src.mTransform);
+ }
+
+ public void createOutputAllocation(Surface surface, int w, int h) {
+
+ if (mRs == null) {
+ return;
+ }
+
+ if (mScrAllocation == null
+ || mScrAllocation.getType().getX() != w
+ || mScrAllocation.getType().getY() != h) {
+ if (mScrAllocation != null) {
+ mScrAllocation.destroy();
+ mScrAllocation = null;
+ Log.v(LOGTAG, " destroy mScrAllocation");
+ }
+ Type.Builder b = new Type.Builder(mRs, Element.RGBA_8888(mRs));
+ b.setX(w);
+ b.setY(h);
+
+ mScrAllocation = Allocation.createTyped(mRs, b.create(),
+ Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
+
+ }
+ mScrAllocation.setSurface(surface);
+
+ if (mzRangeFullAllocation == null
+ || mzRangeFullAllocation.getType().getX() != w
+ || mzRangeFullAllocation.getType().getY() != h) {
+ if (mzRangeFullAllocation != null) {
+ mzRangeFullAllocation.destroy();
+ mzRangeFullAllocation = null;
+ Log.v(LOGTAG, " destroy mzRangeFullAllocation");
+
+ }
+ Type.Builder b = new Type.Builder(mRs, Element.F32_2(mRs));
+ b.setX(w);
+ b.setY(h);
+
+ mzRangeFullAllocation = Allocation.createTyped(mRs, b.create());
+ }
+
+ mImgWidth = w;
+ mImgHeight = h;
+ mTransform.setScreenDim(w, h);
+ }
+
+ public void copyData(VrState src) {
+ mRs = src.mRs;
+ mVolume = src.mVolume;
+ mRsMask = src.mRsMask;
+ mMaterial = src.mMaterial;
+ if (mCubeVolume == null) {
+ mCubeVolume = new Cube();
+ }
+ mCubeVolume.clone(src.mCubeVolume);
+ mCubeScreen = new TriData(src.mCubeScreen); // TODO I should not have to do new each time
+ mImgWidth = src.mImgWidth;
+ mImgHeight = src.mImgHeight;
+ mTransform.clone(src.mTransform);
+
+ }
+
+ public void destroyScreenAllocation() {
+ Log.v(LOGTAG, "destroyScreenAllocation");
+ if (mScrAllocation != null) {
+ mScrAllocation.destroy();
+ mScrAllocation = null;
+ }
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs
new file mode 100644
index 00000000..808722c3
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.vr.engine)
+#pragma rs_fp_relaxed
+
+int brick_dimx;
+int brick_dimy;
+int brick_dimz;
+rs_allocation volume;
+rs_allocation opacity;
+int z_offset;
+// output a single bit per pixel volume based on opacity
+
+uint __attribute__((kernel)) pack_chunk(uint32_t x) {
+
+ int brick = x / (32 * 32);
+ int bx = brick % brick_dimx;
+ int yz = brick / brick_dimx;
+ int by = yz % brick_dimy;
+ int bz = yz / brick_dimy;
+
+ int in_brick = x % (32 * 32);
+ int in_br_y = in_brick % 32;
+ int in_br_z = in_brick / 32;
+ int pz = (bz << 5) | in_br_z;
+
+
+ int py = (by << 5) | in_br_y;
+
+ uint out = 0;
+
+ if (pz >= rsAllocationGetDimZ(volume)) {
+ return out;
+ }
+ if (py >= rsAllocationGetDimY(volume)) {
+ return out;
+ }
+ for (int in_br_x = 0; in_br_x < 32; in_br_x++) {
+ int px = (bx << 5) | in_br_x;
+
+
+ int intensity = 0xFFFF & rsGetElementAt_short(volume, px, py, pz);
+ uchar op = rsGetElementAt_uchar(opacity, intensity);
+ uint v = (op > 0) ? 1 : 0;
+ out |= v << in_br_x;
+ }
+ return out;
+}
+rs_allocation bricks; // input bricks
+
+uint __attribute__((kernel)) dilate(uint in, uint32_t x) {
+
+ int BRICK_SIZE = 32 * 32;
+ int brick = x / (BRICK_SIZE);
+ int bx = brick % brick_dimx;
+ int yz = brick / brick_dimx;
+ int by = yz % brick_dimy;
+ int bz = yz / brick_dimy;
+
+ int in_brick = x % (BRICK_SIZE);
+ int in_br_y = in_brick % 32;
+ int in_br_z = in_brick / 32;
+ uint slice;
+ uint out = in;
+ out |= in >> 1;
+ out |= in << 1;
+ int base_brick = bx + (by + brick_dimy * bz) * brick_dimx;
+ int base_offset = (in_br_z) * 32 + (in_br_y);
+
+ int slice_pos = base_brick * BRICK_SIZE + base_offset;
+
+ // +/- in x
+ if (bx > 0) {
+ slice = 0x80000000 & rsGetElementAt_uint(bricks, slice_pos - BRICK_SIZE);
+ out |= slice >> 31;
+ }
+ if (bx < brick_dimx - 1) {
+ slice = 1 & rsGetElementAt_uint(bricks, slice_pos + BRICK_SIZE);
+ out |= slice << 31;
+ }
+
+ // - in Y
+ int off_neg_y = -1; // simple case -1 slice;
+ if (in_br_y == 0) { // att the edge in brick go to y-1 brick
+ if (by > 0) { // edge of screen
+ off_neg_y = 31 - BRICK_SIZE * brick_dimx;
+ } else {// edge of volume
+ off_neg_y = 0;
+ }
+ }
+
+ slice = rsGetElementAt_uint(bricks, slice_pos + off_neg_y);
+ out |= slice;
+ // + in Y
+ int off_pos_y = 1;
+ if (in_br_y == 31) {
+ if (by < brick_dimy - 1) {
+ off_pos_y = BRICK_SIZE * brick_dimx - 31;
+ } else {
+ off_pos_y = 0;
+ }
+ }
+ slice = rsGetElementAt_uint(bricks, slice_pos + off_pos_y);
+ out |= slice;
+ int off_neg_z = -32;
+ if (in_br_z == 0) {
+ if (bz > 0) { // edge of screen
+ off_neg_z = 31*32 - brick_dimx * brick_dimy* BRICK_SIZE;
+ } else {
+ off_neg_z = 0;
+ }
+ }
+ slice = rsGetElementAt_uint(bricks, slice_pos + off_neg_z);
+ out |= slice;
+ int off_pos_z = 32;
+ if (in_br_z == 31) {
+ if (bz < brick_dimz - 1) {
+ off_pos_z = brick_dimx * brick_dimy * BRICK_SIZE - 31*32;
+ } else {
+ off_pos_z = 0;
+ }
+ }
+ slice = rsGetElementAt_uint(bricks, slice_pos + off_pos_z);
+ out |= slice;
+
+ return out;
+
+
+
+}
+int z;
+
+void __attribute__((kernel)) copy(short in, uint32_t x, uint32_t y) {
+ rsSetElementAt_short(volume, in, x, y, z);
+}
+
+int size;
+
+static float3 nylander(float3 p, int n) {
+ float3 out;
+ float r = length(p);
+ float phi = atan2(p.y, p.x);
+ float theta = acos(p.z / r);
+ float rn = pow(r, n);
+ out.x = sin(n * theta) * cos(n * phi);
+ out.y = sin(n * theta) * sin(n * phi);
+ out.z = cos(n * theta);
+ return out * rn;
+}
+
+/**
+* 8 x faster than the above for n = 3
+*/
+static float3 nylander3(float3 p) {
+ float3 out = (float3){0.f, 0.f, 0.f};
+ float xy2 = p.x * p.x + p.y * p.y;
+ if (xy2 == 0) return out;
+ float z23x2y2 = (3 * p.z * p.z - p.x * p.x - p.y * p.y);
+ out.x = (z23x2y2 * p.x * (p.x * p.x - 3 * p.y * p.y)) / xy2;
+ out.y = (z23x2y2 * p.y * (3 * p.x * p.x - p.y * p.y)) / xy2;
+ out.z = p.z * (p.z * p.z - 3 * p.x * p.x - 3 * p.y * p.y);
+ return out;
+}
+
+static float vsize(float3 p) {
+ return sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
+}
+
+short __attribute__((kernel)) mandelbulb(uint32_t x, uint32_t y) {
+ int size2 = size / 2;
+ if (z < size2) {
+ return 256-4*(size2-z+4)*hypot((float)x-size2,(float)y-size2) / size2 ;
+ }
+ float3 c = (float3) {(float) x, (float) y, (float) z};
+ c = ((c - size2) / (size2 * .9f));
+
+ int loop = 25;
+ float3 p = c;
+ float len;
+ for (int i = 0; i < loop; i++) {
+ // p = nylander(p, 3) + c;
+ p = nylander3(p) + c;
+ len = fast_length(p);
+ if (len > 2.f) return 255 - loop*10;
+ if (len < .3f) return loop*10;
+
+ }
+ len = length(p);
+ return (short) (255 - (len * 255) / 4);
+}
+
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs
new file mode 100644
index 00000000..092a382e
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.vr.engine)
+#pragma rs_fp_relaxed
+
+#define FLOAT_MAX 3.4028234E30f
+
+float vert[3 * 8];
+int index[3 * 12];
+int width;
+int height;
+
+static float zoff[12];
+static float2 slope[12];
+
+static float2 p1[12];
+static float2 p2[12];
+static float2 p3[12];
+
+ float2 d12[12];
+ float2 d23[12];
+ float2 d31[12];
+static int total;
+
+static void
+triangleSetup(float3 f1, float3 f2, float3 f3) {
+ if (((f1.x - f2.x) * (f3.y - f2.y) - (f1.y - f2.y) * (f3.x - f2.x)) < 0) {
+ float3 tmp = f1;
+ f1 = f2;
+ f2 = tmp;
+ }
+
+ // using maxmima
+ // string(solve([x1*dx+y1*dy+zoff=z1,x2*dx+y2*dy+zoff=z2,x3*dx+y3*dy+zoff=z3],[dx,dy,zoff]));
+ double d = (f1.x * (f3.y - f2.y) - f2.x * f3.y + f3.x * f2.y + (f2.x - f3.x)
+ * f1.y);
+
+ if (d == 0) {
+ return;
+ }
+
+ slope[total].x = (-(f1.y * (f3.z - f2.z) - f2.y * f3.z + f3.y * f2.z + (f2.y - f3.y) * f1.z)
+ / d);
+ slope[total].y = ((f1.x * (f3.z - f2.z) - f2.x * f3.z + f3.x * f2.z + (f2.x - f3.x) * f1.z)
+ / d);
+ zoff[total] = ((f1.x * (f3.y * f2.z - f2.y * f3.z) + f1.y * (f2.x * f3.z - f3.x * f2.z) +
+ (f3.x * f2.y - f2.x * f3.y) * f1.z) / d);
+
+ p1[total] = f1.xy;
+ p2[total] = f2.xy;
+ p3[total] = f3.xy;
+ d12[total] = p1[total] - p2[total];
+ d23[total] = p2[total] - p3[total];
+ d31[total] = p3[total] - p1[total];
+ total++;
+}
+
+void setup_triangles(int w, int h) {
+ width = w;
+ height = h;
+ total = 0;
+ // rsDebug("RRRRR >>>>>> setup_triangles ", w, h);
+ float3 f1;
+ float3 f2;
+ float3 f3;
+ for (int i = 0; i < 3 * 12; i += 3) {
+ int p1 = index[i];
+ int p2 = index[i + 1];
+ int p3 = index[i + 2];
+ f1.x = vert[p1];
+ f1.y = vert[p1 + 1];
+ f1.z = vert[p1 + 2];
+ f2.x = vert[p2];
+ f2.y = vert[p2 + 1];
+ f2.z = vert[p2 + 2];
+ f3.x = vert[p3];
+ f3.y = vert[p3 + 1];
+ f3.z = vert[p3 + 2];
+ triangleSetup(f1, f2, f3);
+ }
+}
+
+
+float2 __attribute__ ((kernel)) render_z(uint32_t x, uint32_t y) {
+ float2 out = (float2) {FLOAT_MAX,-FLOAT_MAX};
+ float2 loc;
+ loc.x = x;
+ loc.y = y;
+ for (int i = 0; i < total; i++) {
+ float2 d1 = loc - p1[i];
+ float2 d2 = loc - p2[i];
+ float2 d3 = loc - p3[i];
+
+ float test1 = (d12[i].x) * (d1.y) - (d12[i].y) * (d1.x);
+ float test2 = (d23[i].x) * (d2.y) - (d23[i].y) * (d2.x);
+ float test3 = (d31[i].x) * (d3.y) - (d31[i].y) * (d3.x);
+ // float test = edge(0 , test1) * edge(0 , test2)* edge(0 , test3);
+
+ if (test1 >= 0 &&
+ test2 >= 0 &&
+ test3 >= 0) {
+ float2 delta = slope[i] * loc;
+ float z = zoff[i] + delta.x + delta.y;
+
+ out.x = min(z, out.x);
+ out.y = max(z, out.y);
+ }
+ }
+ return out;
+}
+
+
+rs_allocation z_range_buff;
+float min_z = 1500;
+float max_z = 2300;
+
+void getMinMax() {
+
+ float tmp_min = FLOAT_MAX;
+ float tmp_max = -FLOAT_MAX;;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float2 v = rsGetElementAt_float2(z_range_buff, x, y);
+ if (v.x == FLOAT_MAX) {
+ continue;
+ }
+ tmp_min = min(tmp_min, v.x);
+ tmp_max = max(tmp_max, v.x);
+ }
+ }
+ min_z = tmp_min;
+ max_z = tmp_max;
+ rsDebug("RRRRR >>>>>> getMinMax ", min_z, max_z);
+}
+
+uchar4 __attribute__ ((kernel)) draw_z_buffer(float2 in) {
+
+ if (in.x != FLOAT_MAX) {
+ uchar v = (uchar)(255 * (in.x - min_z) / (max_z - min_z));
+ uchar4 out;
+ out.r = v;
+ out.g = v;
+ out.b = v;
+ out.a = 255;
+ return out;
+ }
+
+ uchar4 out;
+ out.r = 0x44;
+ out.g = 0x44;
+ out.b = 0x99;
+ out.a = 255;
+ return out;
+} \ No newline at end of file
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs
new file mode 100644
index 00000000..997bb102
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.vr.engine)
+#pragma rs_fp_relaxed
+
+#define FLOAT_MAX 3.4028234E30f
+#define JITTER_LENGTH 3456
+float jitter[JITTER_LENGTH];
+
+float3 s;
+float3 dx;
+float3 dy;
+float3 dz;
+float zoomFactor;
+rs_matrix4x4 matrix4;
+rs_matrix3x3 matrix3;
+uchar4 base_color;
+static float3 mLight;
+
+// material color
+rs_allocation opacity;
+rs_allocation color_map;
+
+static void fillJitter() {
+ for (int i = 0; i < JITTER_LENGTH; i++) {
+ jitter[i] = rsRand(1.0f);
+ }
+}
+
+void setup_vectors() {
+ s = rsMatrixMultiply(&matrix4, (float3) {0.5f, 0.5f, 0.5f}).xyz;
+ dx = rsMatrixMultiply(&matrix3, (float3) {1.f, 0.f, 0.f});
+ dy = rsMatrixMultiply(&matrix3, (float3) {0.f, 1.f, 0.f});
+ dz = rsMatrixMultiply(&matrix3, (float3) {0.f, 0.f, 1.f});
+ zoomFactor = dz.x * dz.x + dz.y * dz.y + dz.z * dz.z;
+ zoomFactor /= dx.x * dx.x + dx.y * dx.y + dx.z * dx.z;
+ base_color.r = 0;
+ base_color.g = 0;
+ base_color.b = 0;
+ base_color.a = 255;
+ fillJitter();
+ float3 mLightRelitvePos = (float3) {0, 0.7071f, -0.7071f}; // light relitve to camera
+ mLight = mLightRelitvePos.x + dx + mLightRelitvePos.y * dy + mLightRelitvePos.z * dz;
+ mLight = normalize(mLight);
+}
+
+// old simple version
+static float triLinear_old(short v_0_0_0, short v_0_0_1, short v_0_1_0, short v_0_1_1,
+ short v_1_0_0, short v_1_0_1, short v_1_1_0, short v_1_1_1,
+ float3 delta) {
+ float v_0_0 = v_0_0_0 + delta.x * (v_0_0_1 - v_0_0_0);
+ float v_0_1 = v_0_1_0 + delta.x * (v_0_1_1 - v_0_1_0);
+ float v_1_0 = v_1_0_0 + delta.x * (v_1_0_1 - v_1_0_0);
+ float v_1_1 = v_1_1_0 + delta.x * (v_1_1_1 - v_1_1_0);
+ float v_0 = v_0_0 + delta.y * (v_0_1 - v_0_0);
+ float v_1 = v_1_0 + delta.y * (v_1_1 - v_1_0);
+ float v = v_0 + delta.z * (v_1 - v_0);
+ return v;
+}
+
+// This seemed to improve over above
+static float triLinear(float v_0_0_0, float v_0_0_1, float v_0_1_0, short v_0_1_1,
+ float v_1_0_0, float v_1_0_1, float v_1_1_0, float v_1_1_1,
+ float3 delta) {
+ float v_0_0 = mix(v_0_0_0, v_0_0_1, delta.x);
+ float v_0_1 = mix(v_0_1_0, v_0_1_1, delta.x);
+ float v_1_0 = mix(v_1_0_0, v_1_0_1, delta.x);
+ float v_1_1 = mix(v_1_1_0, v_1_1_1, delta.x);
+ float v_0 = mix(v_0_0, v_0_1, delta.y);
+ float v_1 = mix(v_1_0, v_1_1, delta.y);
+ float v = mix(v_0, v_1, delta.z);
+ return v;
+}
+
+rs_allocation bricks;
+rs_allocation brick_index;
+int brick_dimx;
+int brick_dimy;
+int brick_dimz;
+
+static int isInBrick(int3 p) {
+ int bx = p.x >> 5;
+ int by = p.y >> 5;
+ int bz = p.z >> 5;
+ int brickno = bx + (by + brick_dimy * bz) * brick_dimx;
+
+ brickno *= 32 * 32;
+ int off = brickno + (p.z & 0x1F) * 32 + (p.y & 0x1F);
+ uint slice = rsGetElementAt_uint(bricks, off);
+ return slice & (1 << (p.x & 0x1F));
+}
+
+rs_allocation volume;
+rs_allocation zbuff;
+bool debug = true;
+
+uchar4 __attribute__ ((kernel)) draw_z_buffer(float2 in, uint32_t x, uint32_t y) {
+ uchar4 out = base_color;
+
+ float zsuface = in.x + .5f;
+ float zstart = zsuface;
+ float zend = in.y - 2.f;//0.5f;
+ float zlen = zend - zstart;
+ float step_dist = length(dz);
+
+ if (zstart == FLOAT_MAX || zlen < 0) {
+ return out;
+ }
+
+ float3 p = s + x * dx + y * dy + dz * zstart;
+ float zb = zend;
+ int izlen = (int) zlen;
+ float light = 1;
+ float4 total_color = (float4) {0.f, 0.f, 0.f, 0.f};
+
+ if (false) { // show the walls only used for debuging
+ int3 ip = convert_int3(p);
+ ip = clamp(ip, 4, 500);
+ short pix = rsGetElementAt_short(volume, ip.x, ip.y, ip.z);
+
+ int intensity = (((short) pix) & 0xFFFF);
+ // intensity = clamp(intensity,0,400);
+ uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);
+ int op = rsGetElementAt_uchar(opacity, intensity);
+
+ out.r = color.r;
+ out.g = color.g;
+ out.b = color.b;
+ out.a = 255;
+ return out;
+ }
+ {
+ int3 ip = convert_int3(p);
+
+ if (isInBrick(ip)) { // isInBrick(ip)) {
+
+ float3 delta = p - convert_float3(ip);
+ // TODO switch to rsAllocationVLoadX_short2
+ // short2 tmps = rsAllocationVLoadX_short2(volume, ip.x + 0, ip.y + 0, ip.z + 0);
+ // float2 tmp = convert_float2(tmps);
+ float v_0_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 0);
+ float v_0_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 0);
+
+ float v_0_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);
+ float v_0_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);
+ float v_1_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 1);
+ float v_1_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 1);
+ float v_1_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);
+ float v_1_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);
+ float v = triLinear(v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,
+ v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,
+ delta);
+ int intensity = (((short) v) & 0xFFFF);
+ uchar op = rsGetElementAt_uchar(opacity, intensity);
+
+ if (op != 0) { // near the surface "broken"
+
+ float sdx = rsGetElementAt_float2(zbuff, max(0, (int) x - 1), y).x - in.x;
+ float sdy = rsGetElementAt_float2(zbuff, x, max(0, (int) y - 1)).x - in.x;
+ float dot_prod = sqrt(1 / (1 + (sdy * sdy + sdx * sdx) * zoomFactor));
+ float opf = op / 255.f;
+ uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);
+ uchar4 mat = rsGetElementAt_uchar4(color_map, intensity * 2 + 1);
+ float4 fcolor = convert_float4(color);;
+
+ float ambient = mat.x / 255.f;
+ float specular = mat.y / 255.f;
+ float diffuse = mat.z / 255.f;
+ float lop = (ambient + diffuse * dot_prod) * light * opf;
+ light -= opf;
+ total_color += fcolor * lop;
+ zb = zstart;
+
+ }
+ }
+ }
+ p += dz * rsRand(2.f);
+
+ if (light > 0) {
+ for (int k = 0; k < izlen - 1; k++) {
+
+ int3 ip = convert_int3(p);
+ if (isInBrick(ip)) {
+ float3 delta = p - convert_float3(ip);
+
+ float v_0_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 0);
+ float v_0_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 0);
+ float v_0_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);
+ float v_0_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);
+ float v_1_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 1);
+ float v_1_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 1);
+ float v_1_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);
+ float v_1_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);
+ float v = triLinear(v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,
+ v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,
+ delta);
+ int intensity = (((short) v) & 0xFFFF);
+ uchar op = rsGetElementAt_uchar(opacity, intensity);
+
+ if (op != 0) {
+
+ float3 v;
+ float3 vn;
+
+ float v_0_0_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 0, ip.z + 0);
+ float v_0_1_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 1, ip.z + 0);
+ float v_1_0_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 0, ip.z + 1);
+ float v_1_1_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 1, ip.z + 1);
+ v.x = triLinear(v_0_0_1, v_0_0_2, v_0_1_1, v_0_1_2,
+ v_1_0_1, v_1_0_2, v_1_1_1, v_1_1_2,
+ delta);
+
+ float v_0_0_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 0, ip.z + 0);
+ float v_0_1_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 1, ip.z + 0);
+ float v_1_0_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 0, ip.z + 1);
+ float v_1_1_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 1, ip.z + 1);
+ vn.x = triLinear(v_0_0_n, v_0_0_0, v_0_1_n, v_0_1_0,
+ v_1_0_n, v_1_0_0, v_1_1_n, v_1_1_0,
+ delta);
+
+ float v_0_2_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 2, ip.z + 0);
+ float v_0_2_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 2, ip.z + 0);
+ float v_1_2_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 2, ip.z + 1);
+ float v_1_2_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 2, ip.z + 1);
+ v.y = triLinear(v_0_1_0, v_0_1_1, v_0_2_0, v_0_2_1,
+ v_1_1_0, v_1_1_1, v_1_2_0, v_1_2_1,
+ delta);
+
+ float v_0_n_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);
+ float v_0_n_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);
+ float v_1_n_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);
+ float v_1_n_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);
+ vn.y = triLinear(v_0_n_0, v_0_n_1, v_0_0_0, v_0_0_1,
+ v_1_n_0, v_1_n_1, v_1_0_0, v_1_0_1,
+ delta);
+
+ float v_n_0_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z - 1);
+ float v_n_0_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z - 1);
+ float v_n_1_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z - 1);
+ float v_n_1_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z - 1);
+ vn.z = triLinear(v_n_0_0, v_n_0_1, v_n_1_0, v_n_1_1,
+ v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,
+ delta);
+
+ float v_2_0_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 2);
+ float v_2_0_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 2);
+ float v_2_1_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 2);
+ float v_2_1_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 2);
+ v.z = triLinear(v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,
+ v_2_0_0, v_2_0_1, v_2_1_0, v_2_1_1,
+ delta);
+
+ float3 dv = normalize(v - vn);
+ float dot_prod = dot(dv, dz);
+ float opf = op / 255.f;
+
+ uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);
+ uchar4 mat = rsGetElementAt_uchar4(color_map, intensity * 2 + 1);
+ float4 fcolor = convert_float4(color);;
+
+ // float3 mLight = (float3) {0,-1,0};
+ float3 normal = dv;
+ float3 r = 2 * normal * dot(mLight, normal) - mLight;
+ float spec = dot(r, dz);
+
+ // Eye point in this space is in the direction (0,0,-1)
+ // Spec * Math.pow(R_z , P) lets use power == 2 (cheap)
+
+ float ambient = mat.x / 255.f; // ambient
+ float specular = mat.y / 255.f; // specular not used right now
+ float diffuse = mat.z / 255.f; // diffuse
+ float lop = (ambient + diffuse * dot_prod + specular * pow(spec, 10)) * light * opf;
+ light -= opf;
+ total_color += fcolor * lop;
+
+ zb = zstart + k;
+ if (light <= 0) {
+ break;
+ }
+ }
+ }
+
+ p += dz;
+ }
+ }
+
+ out = convert_uchar4(total_color);
+ out.a = 0xFF;
+
+ return out;
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java
new file mode 100644
index 00000000..7ee2ac9e
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.ScriptC_bricked;
+import com.example.android.rs.vr.engine.Volume;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Vector;
+
+/**
+ * The simplest possible DICOM Reader.
+ * Will only read raw 16 bit dicom slices (the most common type)
+ * If the volume is compressed (usually JPEG2000) you need a decompression tool
+ *
+ * Note: All constants 0xNNNN, 0xNNNN are DICOM TAGS
+ * (see online documentation of DICOM standard)
+ */
+public class LoaderDicom {
+ private static final String LOGTAG = "ReadDicom";
+ String mName;
+ final boolean dbg = false;
+ private ByteOrder mByteOrder;
+ boolean explicit = true;
+ MappedByteBuffer mMappedByteBuffer;
+ long mFileLen;
+ private static final int MIN_VOLUME_SIZE = 20;
+ class Element {
+ int mGroup;
+ int mElement;
+ short mVR;
+ long mLength;
+ Object mValue;
+
+ @Override
+ public String toString() {
+ byte[] vrs = new byte[]{(byte) (mVR & 0xFF), (byte) ((mVR >> 8) & 0xFF)};
+ return Integer.toHexString(mGroup) + "," +
+ Integer.toHexString(mElement) + "(" +
+ new String(vrs) + ") [" + mLength + "] ";
+ }
+ }
+
+ static short vr(String v) {
+ byte[] b = v.getBytes();
+ return (short) (((b[1] & 0xFF) << 8) | (b[0] & 0xFF));
+ }
+
+ static final short OB = vr("OB");
+ static final short OW = vr("OW");
+ static final short OF = vr("OF");
+ static final short SQ = vr("SQ");
+ static final short UT = vr("UT");
+ static final short UN = vr("UN");
+ static final short DS = vr("DS");
+ static final short US = vr("US");
+ static final short AS = vr("AS");
+ static final short AT = vr("AT");
+ static final short CS = vr("CS");
+ static final short DA = vr("DA");
+ static final short DT = vr("DT");
+ static final short FL = vr("FL");
+ static final short FD = vr("FD");
+ static final short IS = vr("IS");
+ static final short LO = vr("LO");
+ static final short LT = vr("LT");
+ static final short PN = vr("PN");
+ static final short SH = vr("SH");
+ static final short SL = vr("SL");
+ static final short SS = vr("SS");
+ static final short ST = vr("ST");
+ static final short TM = vr("TM");
+ static final short UI = vr("UI");
+ static final short UL = vr("UL");
+ static final short AE = vr("AE");
+
+ static HashSet<Short> strVRs = new HashSet<Short>();
+
+ static {
+ short[] all = new short[]{
+ AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, UT
+ };
+ for (short anAll : all) {
+ strVRs.add(anAll);
+ }
+ }
+
+ boolean str(short vr) {
+ return strVRs.contains(vr);
+ }
+
+ boolean big(short vr) {
+ return OB == vr || OW == vr || OF == vr || SQ == vr || UT == vr || UN == vr;
+ }
+
+ class TagSet extends HashMap<Integer, Element> {
+ Element get(int group, int element) {
+ return get(tagInt(group, element));
+ }
+
+ void put(Element e) {
+ put(tagInt(e.mGroup, e.mElement), e);
+ }
+ }
+
+ static int tagInt(int g, int e) {
+ return (g << 16) | (e & 0xFFFF);
+ }
+
+ public static ByteOrder reverse(ByteOrder o) {
+ return (o == ByteOrder.LITTLE_ENDIAN) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
+ }
+
+ public TagSet read(File file, int[] tags) throws Exception {
+ mName = file.getName();
+ TagSet set = new TagSet();
+ HashSet<Integer> toAdd = new HashSet<Integer>();
+ for (int n : tags) {
+ toAdd.add(n);
+ }
+ RandomAccessFile f = new RandomAccessFile(file, "r");
+
+ mMappedByteBuffer = f.getChannel().map(MapMode.READ_ONLY, 0, mFileLen = f.length());
+ mMappedByteBuffer.position(132);
+ setOrder(ByteOrder.LITTLE_ENDIAN);
+ Element e = new Element();
+ boolean early = true;
+
+ while (mMappedByteBuffer.position() < mFileLen) {
+ int pos = mMappedByteBuffer.position();
+ int jump = (int) readTag(e);
+
+ if (early) {
+ if (e.mGroup > 255) {
+ setOrder((mByteOrder == ByteOrder.LITTLE_ENDIAN) ?
+ ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
+ mMappedByteBuffer.position(mMappedByteBuffer.position() - jump);
+ readTag(e);
+ }
+ }
+
+ if (early && e.mGroup >= 8) {
+
+ early = false;
+ }
+ if (toAdd.contains(tagInt(e.mGroup, e.mElement))) {
+ readValue(e);
+ set.put(e);
+ if (e.mGroup == 0x7fe0 && e.mElement == 0x10) {
+ return set;
+ }
+ e = new Element();
+
+ } else {
+ if (e.mGroup == 0x7fe0 && e.mElement == 0x10) {
+ return set;
+ }
+
+ skipValue(e);
+ }
+ }
+ return set;
+ }
+
+ private long readTag(Element e) {
+ e.mGroup = mMappedByteBuffer.getShort() & 0xFFFF;
+ e.mElement = mMappedByteBuffer.getShort() & 0xFFFF;
+
+ if (e.mGroup == 0xFFFE && e.mElement == 0xE000) {
+ e.mLength = mMappedByteBuffer.getInt();
+ if (e.mLength == -1) {
+ e.mLength = 0;
+ }
+ e.mVR = vr("s<");
+ return 8;
+ }
+
+ if (explicit) {
+ e.mVR = mMappedByteBuffer.getShort();
+
+ if (big(e.mVR)) {
+ mMappedByteBuffer.getShort();
+ e.mLength = mMappedByteBuffer.getInt() & 0xFFFFFFFF;
+ } else {
+ e.mLength = mMappedByteBuffer.getShort() & 0xFFFF;
+ }
+ } else {
+ e.mVR = 0;
+ int len = mMappedByteBuffer.getInt();
+ e.mLength = (len) & 0xFFFFFFFFL;
+ if (0xFFFFFFFF == e.mLength) {
+ Log.v(LOGTAG, "undefined");
+ e.mLength = 0;
+ }
+ }
+ if (e.mLength == -1 || e.mLength == 65535) {
+ e.mLength = 0;
+ }
+ return 8;
+ }
+
+ private void skipValue(Element e) {
+ if (e.mLength == 0) {
+ return;
+ }
+ if (dbg && str(e.mVR)) {
+ mMappedByteBuffer.get(readBuff, 0, (int) (e.mLength));
+ e.mValue = new String(readBuff, 0, (int) (e.mLength));
+ // Log.v(LOGTAG, e + " " + e.mValue);
+ } else {
+ mMappedByteBuffer.position((int) (mMappedByteBuffer.position() + e.mLength));
+ }
+ }
+
+ byte[] readBuff = new byte[200];
+
+ private void readValue(Element e) {
+ if (str(e.mVR)) {
+ mMappedByteBuffer.get(readBuff, 0, (int) (e.mLength));
+ e.mValue = new String(readBuff, 0, (int) (e.mLength));
+ } else if (e.mVR == US) {
+ e.mValue = new Short(mMappedByteBuffer.getShort());
+ } else if (e.mVR == OW) {
+ if (e.mLength == -1) {
+ e.mLength = mFileLen - mMappedByteBuffer.position();
+ }
+ short[] s = new short[(int) (e.mLength / 2)];
+ mMappedByteBuffer.asShortBuffer().get(s);
+ e.mValue = s;
+ }
+
+ }
+
+ private void setOrder(ByteOrder order) {
+ mByteOrder = order;
+ mMappedByteBuffer.order(mByteOrder);
+ }
+
+ public static Volume buildVolume(String dirName) {
+ return buildVolume(new File(dirName));
+ }
+
+ public static Volume buildVolume(File dir) {
+ LoaderDicom d = new LoaderDicom();
+ int[] tags = new int[]{
+ tagInt(0x20, 0x32),
+ tagInt(0x20, 0x37),
+ tagInt(0x28, 0x10),
+ tagInt(0x28, 0x11),
+ tagInt(0x7fe0, 0x10)
+ };
+
+ File[] files = dir.listFiles();
+ Arrays.sort(files, new Comparator<File>() {
+
+ @Override
+ public int compare(File o1, File o2) {
+
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ Volume v = new Volume();
+ int count = 0;
+ for (File file : files) {
+ if (file.isDirectory()) {
+ continue;
+ }
+ if (file.getName().equals(".DS_Store")) {
+ continue;
+ }
+ count++;
+ }
+ if (count < MIN_VOLUME_SIZE) {
+ return null;
+ }
+ v.mData = new short[count][];
+ v.mDimz = count;
+ count = 0;
+ for (File file : files) {
+ if (file.isDirectory()) {
+ continue;
+ }
+ if (file.getName().equals(".DS_Store")) {
+ continue;
+ }
+ try {
+ TagSet data = d.read(file, tags);
+ v.mData[count] = (short[]) data.get(0x7fe0, 0x10).mValue;
+ count++;
+ v.mDimx = (Short) data.get(0x28, 0x10).mValue;
+ v.mDimy = (Short) data.get(0x28, 0x11).mValue;
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Failed to parse " + file.getPath());
+ e.printStackTrace();
+ }
+ }
+ return v;
+ }
+
+ /**
+ * This is a multi threaded volume loaded
+ * It creates 2xthe number of cores
+ * @param rs The renderscript context
+ * @param dir The directory containing the DICOM files
+ * @param listener The Listener to provide feedback to the UI on loading
+ * @return The Volume object loaded with the volume
+ */
+ public static Volume buildRSVolume(final RenderScript rs, File dir,
+ final VolumeLoader.ProgressListener listener) {
+ final int[] tags = new int[]{
+ tagInt(0x20, 0x32),
+ tagInt(0x20, 0x37),
+ tagInt(0x28, 0x10),
+ tagInt(0x28, 0x11),
+ tagInt(0x28, 0x30),
+ tagInt(0x7fe0, 0x10)
+ };
+
+ File[] files = dir.listFiles();
+ Arrays.sort(files, new Comparator<File>() {
+
+ @Override
+ public int compare(File o1, File o2) {
+
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ final Volume v = new Volume();
+ int count = 0;
+
+
+ final Vector<File> toRun = new Vector<File>();
+ final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ continue;
+ }
+ if (file.getName().equals(".DS_Store")) {
+ continue;
+ }
+ toRun.add(file);
+ fileMap.put(file, count);
+ count++;
+ }
+ if (count < MIN_VOLUME_SIZE) {
+ return null;
+ }
+ v.mDimz = count;
+ if (listener != null) {
+ listener.progress(0, v.mDimx);
+ }
+ v.mVolumeAllocation = null;
+ final String []pixel_spacing = new String[count];
+ final String []slice_pos = new String[count];
+
+ final ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+ int number_of_threads = 2 * Runtime.getRuntime().availableProcessors();
+ Thread[] t = new Thread[number_of_threads];
+ for (int i = 0; i < number_of_threads; i++) {
+
+ t[i] = new Thread() {
+ LoaderDicom d = new LoaderDicom();
+
+
+ private File getOne() {
+ synchronized (toRun) {
+ if (toRun.isEmpty()) {
+ return null;
+ }
+ return toRun.remove(0);
+ }
+ }
+
+ public void run() {
+ File file;
+
+ Allocation alloc_slice = null;
+
+ while ((file = getOne()) != null) {
+ int z = fileMap.get(file);
+ try {
+ TagSet data = d.read(file, tags);
+ short[] slice = (short[]) data.get(0x7fe0, 0x10).mValue;
+ short dimX = (Short) data.get(0x28, 0x10).mValue;
+ short dimY = (Short) data.get(0x28, 0x11).mValue;
+ String val;
+ val = (String) data.get(0x28,0x30).mValue;
+ pixel_spacing[z] = val;
+
+ val = (String) data.get(0x20,0x32).mValue;
+ slice_pos[z] = val;
+
+ if (v.mDimx == -1) {
+ v.mDimy = dimY;
+ v.mDimx = dimX;
+ }
+ synchronized (v) {
+ if (v.mVolumeAllocation == null) {
+ Type.Builder b = new Type.Builder(rs,
+ android.renderscript.Element.I16(rs));
+ b.setX(v.mDimx).setY(v.mDimy);
+ b.setZ(v.mDimz);
+ v.mVolumeAllocation = Allocation.createTyped(rs, b.create(),
+ Allocation.USAGE_SCRIPT);
+ scriptC_bricked.set_volume(v.mVolumeAllocation);
+ }
+ }
+
+ if (alloc_slice == null) {
+ Type.Builder b = new Type.Builder(rs,
+ android.renderscript.Element.I16(rs));
+ b.setX(v.mDimx).setY(v.mDimy);
+ alloc_slice = Allocation.createTyped(rs, b.create(),
+ Allocation.USAGE_SCRIPT);
+ }
+ if (listener != null) {
+ listener.progress(z, v.mDimx);
+ }
+ int size = v.mDimy * v.mDimx;
+ alloc_slice.copyFromUnchecked(slice);
+ synchronized (v) {
+ scriptC_bricked.set_z(z);
+ scriptC_bricked.forEach_copy(alloc_slice);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ alloc_slice.destroy();
+ }
+ };
+ t[i].start();
+ }
+
+ for (int i = 0; i < number_of_threads; i++) {
+ try {
+ t[i].join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ String[]pss = pixel_spacing[0].split("\\\\");
+ String[]s1ps = slice_pos[0].split("\\\\");
+ String[]s2ps = slice_pos[1].split("\\\\");
+ float sx = Float.parseFloat(pss[0]);
+ float sy = Float.parseFloat(pss[1]);
+ double dzx = Double.parseDouble(s1ps[0]) - Double.parseDouble(s2ps[0]);
+ double dzy = Double.parseDouble(s1ps[1]) - Double.parseDouble(s2ps[1]);
+ double dzz = Double.parseDouble(s1ps[2]) - Double.parseDouble(s2ps[2]);
+ float sz = (float) Math.hypot(dzx,Math.hypot(dzy,dzz));
+ float min = Math.min(sx,Math.min(sy,sz));
+ v.mVoxelDim[0] = sx/min;
+ v.mVoxelDim[1] = sy/min;
+ v.mVoxelDim[2] = sz/min;
+ Log.v(LOGTAG,"LOADING DONE ....");
+ scriptC_bricked.destroy();
+ return v;
+ }
+
+ /**
+ * Single threaded version of the volume createor
+ * @param rs the renderscript context
+ * @param dir the directory containing the dicom files
+ * @param listener used to feed back status to progress listeners
+ * @return Built volume
+ */
+ public static Volume buildRSVolume2(final RenderScript rs, File dir,
+ VolumeLoader.ProgressListener listener) {
+ final int[] tags = new int[]{
+ tagInt(0x20, 0x32),
+ tagInt(0x20, 0x37),
+ tagInt(0x28, 0x10),
+ tagInt(0x28, 0x11),
+ tagInt(0x28, 0x30),
+ tagInt(0x7fe0, 0x10)
+ };
+ File[] files = dir.listFiles();
+ Arrays.sort(files, new Comparator<File>() {
+
+ @Override
+ public int compare(File o1, File o2) {
+
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ Volume v = new Volume();
+ int count = 0;
+
+
+ final Vector<File> toRun = new Vector<File>();
+ final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();
+ for (File file1 : files) {
+ if (file1.isDirectory()) {
+ continue;
+ }
+ if (file1.getName().equals(".DS_Store")) {
+ continue;
+ }
+ toRun.add(file1);
+ fileMap.put(file1, count);
+ count++;
+ }
+ if (count < 20) {
+ return null;
+ }
+ v.mDimz = count;
+ if (listener != null) {
+ listener.progress(0, v.mDimz);
+ }
+ v.mVolumeAllocation = null;
+ Allocation alloc_slice = null;
+ ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+ LoaderDicom d = new LoaderDicom();
+ String pixel_spacing = null;
+ String slice1_pos = null;
+ String slice2_pos = null;
+ boolean slice_spacing_set = false;
+ int z = 0;
+ for (File file : toRun) {
+ try {
+ TagSet data = d.read(file, tags);
+ short[] slice = (short[]) data.get(0x7fe0, 0x10).mValue;
+ short mDimx = (Short) data.get(0x28, 0x10).mValue;
+ short mDimy = (Short) data.get(0x28, 0x11).mValue;
+ String val;
+ val = (String) data.get(0x28,0x30).mValue;
+ if (val != null && pixel_spacing==null) {
+ pixel_spacing = val;
+ }
+ val = (String) data.get(0x20,0x32).mValue;
+ if (val != null) {
+ if (slice1_pos == null) {
+ slice1_pos = val;
+ } else if (slice2_pos == null) {
+ slice2_pos = val;
+ }
+ }
+ if (v.mDimx == -1) {
+ v.mDimy = mDimy;
+ v.mDimx = mDimx;
+ }
+
+ if (v.mVolumeAllocation == null) {
+ Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));
+ b.setX(v.mDimx).setY(v.mDimy);
+ alloc_slice = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+ b.setZ(v.mDimz);
+ v.mVolumeAllocation = Allocation.createTyped(rs, b.create(),
+ Allocation.USAGE_SCRIPT);
+ scriptC_bricked.set_volume(v.mVolumeAllocation);
+
+ }
+ if (listener != null) {
+ listener.progress(z, v.mDimz);
+ }
+
+ int size = v.mDimy * v.mDimx;
+ alloc_slice.copyFromUnchecked(slice);
+ scriptC_bricked.set_z(z);
+ scriptC_bricked.forEach_copy(alloc_slice);
+ z++;
+ if (!slice_spacing_set
+ && pixel_spacing!=null
+ && slice1_pos!=null
+ && slice2_pos != null) {
+ String[]pss = pixel_spacing.split("\\\\");
+ String[]s1ps = slice1_pos.split("\\\\");
+ String[]s2ps = slice2_pos.split("\\\\");
+ float sx = Float.parseFloat(pss[0]);
+ float sy = Float.parseFloat(pss[1]);
+ double dzx = Double.parseDouble(s1ps[0]) - Double.parseDouble(s2ps[0]);
+ double dzy = Double.parseDouble(s1ps[1]) - Double.parseDouble(s2ps[1]);
+ double dzz = Double.parseDouble(s1ps[2]) - Double.parseDouble(s2ps[2]);
+ float sz = (float) Math.hypot(dzx,Math.hypot(dzy,dzz));
+ float min = Math.min(sx,Math.min(sy,sz));
+ v.mVoxelDim[0] = sx/min;
+ v.mVoxelDim[1] = sy/min;
+ v.mVoxelDim[2] = sz/min;
+ slice_spacing_set = true;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ Log.v(LOGTAG,"LOADING DONE ....");
+
+ alloc_slice.destroy();
+
+ scriptC_bricked.destroy();
+ return v;
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java
new file mode 100644
index 00000000..c58d0ec3
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.ScriptC_bricked;
+import com.example.android.rs.vr.engine.Volume;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * Created by hoford on 2/2/15.
+ */
+public class LoaderRaw {
+ private static final String LOGTAG = "RawLoader";
+
+ /**
+ * This builds the volume based on a collection of raw image files
+ * @param rs The Renderscript context
+ * @param dir The directory containing the raw images
+ * @param prop property object containing information about the files
+ * @param listener To provide feedback
+ * @return The created volume
+ */
+ public static Volume buildRSVolume(final RenderScript rs, File dir, Properties prop,
+ final VolumeLoader.ProgressListener listener) {
+ String[] dim = prop.getProperty("dim").split("x");
+ Volume v = new Volume();
+ v.mDimx = Integer.parseInt(dim[0]);
+ v.mDimy = Integer.parseInt(dim[1]);
+ v.mDimz = Integer.parseInt(dim[2]);
+ String[] voxeldim = prop.getProperty("voxeldim").split(",");
+ v.mVoxelDim[0] = Float.parseFloat(voxeldim[0]);
+ v.mVoxelDim[1] = Float.parseFloat(voxeldim[1]);
+ v.mVoxelDim[2] = Float.parseFloat(voxeldim[2]);
+ Float min = Math.min(v.mVoxelDim[0], Math.min(v.mVoxelDim[1], v.mVoxelDim[2]));
+ v.mVoxelDim[0] /= min;
+ v.mVoxelDim[1] /= min;
+ v.mVoxelDim[2] /= min;
+ listener.progress(0, v.mDimz);
+ if (v.mDimz < 20) {
+ return null;
+ }
+ Log.v(LOGTAG, "Loading " + dir.getPath());
+ File[] f = dir.listFiles();
+ Log.v(LOGTAG, "dir contains " + f.length + " files");
+ Arrays.sort(f, new Comparator<File>() {
+
+ @Override
+ public int compare(File o1, File o2) {
+
+ return Integer.decode(o1.getName()).compareTo(Integer.decode(o2.getName()));
+ }
+ });
+
+ int count = 0;
+
+
+ final Vector<File> toRun = new Vector<File>();
+ final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();
+ for (int i = 0; i < f.length; i++) {
+ if (f[i].isDirectory()) {
+ continue;
+ }
+
+ toRun.add(f[i]);
+ fileMap.put(f[i], count);
+ count++;
+ }
+
+ v.mDimz = count;
+ if (listener != null) {
+ listener.progress(0, v.mDimz);
+ }
+
+ v.mVolumeAllocation = null;
+ Allocation alloc_slice = null;
+ ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+ FileInputStream inputStream;
+ String pixel_spacing = null;
+ String slice1_pos = null;
+ String slice2_pos = null;
+ boolean slice_spacing_set = false;
+ int z = 0;
+ for (File file : toRun) {
+ try {
+ inputStream = new FileInputStream(file);
+ MappedByteBuffer mbb = inputStream.getChannel().map(FileChannel.MapMode.READ_ONLY,
+ 0, v.mDimy * v.mDimx * 2);
+ short[] slice = new short[v.mDimy * v.mDimx];
+ mbb.asShortBuffer().get(slice);
+ inputStream.close();
+ mbb = null;
+ if (v.mVolumeAllocation == null) {
+ Log.v(LOGTAG, "make Volume " + z);
+ Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));
+ b.setX(v.mDimx).setY(v.mDimy);
+ alloc_slice = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+ b.setZ(v.mDimz);
+ v.mVolumeAllocation = Allocation.createTyped(rs,
+ b.create(), Allocation.USAGE_SCRIPT);
+ scriptC_bricked.set_volume(v.mVolumeAllocation);
+
+ }
+ Log.v(LOGTAG, "LOAD SLICE " + z);
+ int size = v.mDimy * v.mDimx;
+ alloc_slice.copyFromUnchecked(slice);
+ scriptC_bricked.set_z(z);
+ scriptC_bricked.forEach_copy(alloc_slice);
+ z++;
+ if (listener != null) {
+ listener.progress(z, v.mDimz);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ rs.finish();
+ alloc_slice.destroy();
+ Log.v(LOGTAG,"LOADING DONE ....");
+
+ scriptC_bricked.destroy();
+ return v;
+ }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java
new file mode 100644
index 00000000..7d058914
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.ScriptC_bricked;
+import com.example.android.rs.vr.engine.Volume;
+
+/**
+ * Provides a simple an example of a computed data set and allows the application to
+ * be run without any data sets.
+ */
+public class Mandelbulb {
+ private static final String LOGTAG = "RawLoader";
+ private static final String simpleLook = "simple";
+ private static final int[][] simpleOpacity = {{120, 0x0}, {126, 0xFF}};
+ private static final int[][] simpleColor = {
+ {200, 0x44AA44, 30, 70, 0},
+ {230, 0xAA44AA, 30, 70, 0},
+ {255, 0xAAAAAA, 30, 70, 0}};
+
+ private static final int SIZE = 512;
+ public static final String NAME = "A Mandelbulb";
+
+ public static Volume buildRSVolume(RenderScript rs,
+ final VolumeLoader.ProgressListener listener) {
+ ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+
+ Volume v = new Volume();
+ v.mDimx = v.mDimy = v.mDimz = SIZE;
+ v.mVoxelDim[0] = v.mVoxelDim[1] = v.mVoxelDim[2] = 1.f;
+ v.addLook(simpleLook, simpleColor, simpleOpacity);
+
+ Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));
+ b.setX(v.mDimx).setY(v.mDimy);
+ Allocation tmp = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+ b.setZ(v.mDimz);
+ b.setX(v.mDimx).setY(v.mDimy).setZ(v.mDimz);
+ v.mVolumeAllocation = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+
+ scriptC_bricked.set_volume(v.mVolumeAllocation);
+ scriptC_bricked.set_size(SIZE);
+ long time = System.nanoTime();
+ for (int z = 0; z < v.mDimz; z++) {
+ scriptC_bricked.set_z(z);
+ scriptC_bricked.forEach_mandelbulb(tmp);
+ scriptC_bricked.forEach_copy(tmp);
+ rs.finish();
+ listener.progress(z, v.mDimz);
+ }
+
+ Log.v(LOGTAG, "compute Mandelbulb in" + ((System.nanoTime() - time) / 1E9f) + "seconds");
+ tmp.destroy();
+ scriptC_bricked.destroy();
+ return v;
+ }
+
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java
new file mode 100644
index 00000000..2321e294
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.Volume;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Properties;
+
+public class VolumeLoader {
+ private static final String LOGTAG = "VolumeLoader";
+ HashMap<String, Properties> map = new HashMap<String, Properties>();
+ File baseDir;
+ ProgressListener mListener;
+
+ public VolumeLoader(String dir) {
+ map.put(Mandelbulb.NAME,null);
+
+ baseDir = new File(dir);
+ if (!baseDir.exists()) {
+ Log.e(LOGTAG, "Directory: \""+dir+"\" does not exist ");
+ return;
+ }
+ Properties[] prop = getPropertyFiles(baseDir);
+ for (int i = 0; i < prop.length; i++) {
+ map.put(prop[i].getProperty("name"), prop[i]);
+ }
+
+ }
+
+ public String[] getNames() {
+ String [] ret = map.keySet().toArray(new String[map.size()]);
+ Arrays.sort(ret);
+ return ret;
+ }
+
+ public Volume getVolume(RenderScript rs, String name) {
+ if (name.equals(Mandelbulb.NAME)) {
+ return Mandelbulb.buildRSVolume(rs,mListener);
+ }
+ Properties p = map.get(name);
+ if (p == null) {
+ Log.v(LOGTAG,"Could not find "+name);
+ return null;
+ }
+ String dir = p.getProperty("dir");
+ Log.v(LOGTAG,"dir ="+dir);
+
+ if ("dicom".equalsIgnoreCase(p.getProperty("format"))) {
+ Log.v(LOGTAG,"processing dicom");
+ Volume v = LoaderDicom.buildRSVolume(rs, new File(baseDir, dir), mListener);
+ String [] looks = p.getProperty("looks").split(",");
+ for (int j = 0; j < looks.length; j++) {
+ String look_color = p.getProperty(looks[j]+".color");
+ String look_opacity = p.getProperty(looks[j]+".opacity");
+ v.addLook(looks[j],look_color,look_opacity);
+ }
+ return v;
+ } else if ("raw".equalsIgnoreCase(p.getProperty("format"))) {
+ Log.v(LOGTAG,"processing dicom");
+ Volume v = LoaderRaw.buildRSVolume(rs, new File(baseDir, dir), p, mListener);
+ String [] looks = p.getProperty("looks").split(",");
+ for (int j = 0; j < looks.length; j++) {
+ String look_color = p.getProperty(looks[j]+".color");
+ String look_opacity = p.getProperty(looks[j]+".opacity");
+ v.addLook(looks[j],look_color,look_opacity);
+ }
+ return v;
+ }
+ Log.v(LOGTAG,"could recognize format");
+ return null;
+ }
+
+ static Properties[] getPropertyFiles(File dir) {
+
+ File[] f = dir.listFiles(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ Log.v(LOGTAG, name);
+ return name.endsWith(".prop");
+ }
+ });
+ Properties[]ret = new Properties[f.length];
+ for (int i = 0; i < f.length; i++) {
+ Properties prop = new Properties();
+ ret[i] = prop;
+ try {
+ prop.load(new FileReader(f[i]));
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return ret;
+ }
+
+ public void setProgressListener(ProgressListener listener){
+ mListener = listener;
+ }
+
+ public static interface ProgressListener {
+ public void progress(int n, int total);
+ }
+}