├── .clang-format ├── .gitignore ├── Doxyfile ├── Objectify.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── de │ │ └── hsrm │ │ └── objectify │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── hsrm │ │ └── objectify │ │ ├── activities │ │ ├── CameraActivity.java │ │ ├── MainActivity.java │ │ ├── ReconstructionDetailActivity.java │ │ ├── ReconstructionListActivity.java │ │ ├── adapter │ │ │ └── ReconstructionListAdapter.java │ │ └── fragments │ │ │ ├── ImageViewerFragment.java │ │ │ ├── ModelViewerFragment.java │ │ │ └── ReconstructionListFragment.java │ │ ├── camera │ │ ├── CameraPreview.java │ │ └── Constants.java │ │ ├── database │ │ ├── DatabaseAdapter.java │ │ └── DatabaseProvider.java │ │ ├── export │ │ └── OBJExport.java │ │ ├── math │ │ ├── Matrix4f.java │ │ ├── Quat4f.java │ │ └── Vector3f.java │ │ ├── rendering │ │ ├── ObjectModel.java │ │ ├── ReconstructionService.java │ │ └── TouchSurfaceView.java │ │ └── utils │ │ ├── ArcBall.java │ │ ├── ArrayUtils.java │ │ ├── BitmapUtils.java │ │ ├── CameraUtils.java │ │ ├── Size.java │ │ └── Storage.java │ ├── res │ ├── drawable-hdpi │ │ ├── ic_action_export.png │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ ├── ic_action_export.png │ │ └── ic_launcher.png │ ├── drawable-nodpi │ │ ├── background.png │ │ ├── camera_lighting_black.9.png │ │ ├── camera_lighting_bottom.9.png │ │ ├── camera_lighting_left.9.png │ │ ├── camera_lighting_right.9.png │ │ ├── camera_lighting_top.9.png │ │ └── lighting_mask.9.png │ ├── drawable-xhdpi │ │ ├── ic_action_export.png │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ ├── ic_action_export.png │ │ └── ic_launcher.png │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ ├── drawable │ │ └── background_noise.xml │ ├── layout │ │ ├── activity_camera.xml │ │ ├── activity_main.xml │ │ ├── activity_reconstruction_detail.xml │ │ ├── activity_reconstruction_list.xml │ │ ├── activity_reconstruction_twopane.xml │ │ ├── fragment_height_map_view.xml │ │ ├── fragment_input_images_view.xml │ │ ├── fragment_model_viewer.xml │ │ ├── fragment_normal_map_view.xml │ │ └── subtitled_spinner_item.xml │ ├── menu │ │ ├── menu_main.xml │ │ └── reconstruction.xml │ ├── values-land │ │ └── dimens.xml │ ├── values-large │ │ └── refs.xml │ ├── values-sw600dp │ │ └── refs.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-w820dp │ │ └── dimens.xml │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── web_hi_res_512.png │ └── rs │ └── de │ └── hsrm │ └── objectify │ └── rendering │ ├── compute_normals.rs │ └── lh_integration.rs ├── build.gradle ├── doc ├── Sequenzdiagramm-Rekonstruktionsvorgang.png ├── Sequenzdiagramm-Rekonstruktionsvorgang.svg ├── app-overview.jpg └── screenshot-app.jpg ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.clang-format: -------------------------------------------------------------------------------- 1 | # Clang-Format Style Options for 3.7+ 2 | --- 3 | # We'll use defaults from the Google style, but with 4 columns indentation. 4 | BasedOnStyle: Google 5 | IndentWidth: 4 6 | --- 7 | Language: Cpp 8 | AccessModifierOffset: -2 9 | AlignConsecutiveAssignments: true 10 | AllowShortBlocksOnASingleLine: true 11 | AllowShortFunctionsOnASingleLine: Empty 12 | KeepEmptyLinesAtTheStartOfBlocks: true 13 | SpacesBeforeTrailingComments: 1 14 | SpaceAfterCStyleCast: true 15 | NamespaceIndentation: All 16 | Standard: Cpp11 17 | --- 18 | Language: JavaScript 19 | ColumnLimit: 100 20 | --- 21 | Language: Java 22 | ColumnLimit: 90 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/ 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /Objectify.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Objectify 2 | The first mobile 3D scanner for Android. 3 | 4 | With this app you can capture 3D models of yourself, friends or family members 5 | and share the results afterwards. Objectify uses the front-facing camera and 6 | the smartphone display to create light reflections to calculate a 3D model 7 | from the picture. You can achieve best results in a very dark environment. 8 | 9 | ![Screenshot Objectify](doc/screenshot-app.jpg) 10 | 11 | This app was initially developed as part of my bachelor thesis at the RheinMain 12 | University of Applied Sciences in Wiesbaden and later released at the Google 13 | Play Store. Since I don't have time anymore to actively support or develop this 14 | app, I've decided to open source it. 15 | 16 | ![Application Overview](doc/app-overview.jpg) 17 | 18 | ## Media attention 19 | 20 | * [http://www.aptgetupdate.de/2011/08/19/objectify-mobiler-3d-scanner-fur-android/](http://www.aptgetupdate.de/2011/08/19/objectify-mobiler-3d-scanner-fur-android/) 21 | * [http://www.netzwelt.de/download/foto-grafik/3d-software-rendering/index.html](http://www.netzwelt.de/download/foto-grafik/3d-software-rendering/index.html) 22 | * [https://www.mendeley.com/research/3drekonstruktion-auf-einem-smartphone-mittels-photometric-stereo/](https://www.mendeley.com/research/3drekonstruktion-auf-einem-smartphone-mittels-photometric-stereo/) 23 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .settings/ 3 | .DS_Store 4 | app/build 5 | .gradle/ 6 | .idea/ -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | apply plugin: 'com.android.application' 7 | 8 | android { 9 | compileSdkVersion 22 10 | buildToolsVersion "22.0.1" 11 | 12 | defaultConfig { 13 | applicationId "de.hsrm.objectify" 14 | minSdkVersion 16 15 | targetSdkVersion 22 16 | renderscriptTargetApi 22 17 | versionCode 3 18 | versionName "1.2" 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | compile group: 'org.ejml', name: 'all', version: '0.27' 31 | compile 'com.android.support:appcompat-v7:22.0.0' 32 | } 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/kai/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/de/hsrm/objectify/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify; 7 | 8 | import android.app.Application; 9 | import android.test.ApplicationTestCase; 10 | 11 | /** 12 | * Testing 13 | * Fundamentals 14 | */ 15 | public class ApplicationTest extends ApplicationTestCase { 16 | public ApplicationTest() { 17 | super(Application.class); 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 41 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/CameraActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities; 7 | 8 | import android.app.Activity; 9 | import android.content.Intent; 10 | import android.content.res.Resources; 11 | import android.graphics.Bitmap; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Matrix; 14 | import android.graphics.Point; 15 | import android.graphics.drawable.NinePatchDrawable; 16 | import android.hardware.Camera; 17 | import android.hardware.Camera.CameraInfo; 18 | import android.hardware.Camera.PictureCallback; 19 | import android.os.AsyncTask; 20 | import android.os.Bundle; 21 | import android.os.Handler; 22 | import android.view.Display; 23 | import android.view.Surface; 24 | import android.view.View; 25 | import android.view.ViewGroup.LayoutParams; 26 | import android.view.Window; 27 | import android.view.WindowManager; 28 | import android.widget.Button; 29 | import android.widget.ImageView; 30 | import android.widget.LinearLayout; 31 | 32 | import java.util.ArrayList; 33 | 34 | import de.hsrm.objectify.R; 35 | import de.hsrm.objectify.camera.CameraPreview; 36 | import de.hsrm.objectify.camera.Constants; 37 | import de.hsrm.objectify.rendering.ReconstructionService; 38 | import de.hsrm.objectify.utils.BitmapUtils; 39 | import de.hsrm.objectify.utils.CameraUtils; 40 | import de.hsrm.objectify.utils.Size; 41 | import de.hsrm.objectify.utils.Storage; 42 | 43 | public class CameraActivity extends Activity { 44 | public static final String RECONSTRUCTION = "new_reconstruction"; 45 | private CameraPreview mCameraPreview; 46 | private ImageView mCameraLighting; 47 | private ImageView mCameraLightingMask; 48 | private Button mTriggerPicturesButton; 49 | private LinearLayout mProgressScreen; 50 | private Camera mCamera; 51 | private String mDirName; 52 | private int mImgCounter; 53 | private int mCameraRotation; 54 | private ArrayList mLightSourcesList; 55 | 56 | @Override 57 | protected void onCreate(Bundle savedInstanceState) { 58 | super.onCreate(savedInstanceState); 59 | 60 | /* make the activity fullscreen */ 61 | requestWindowFeature(Window.FEATURE_NO_TITLE); 62 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 63 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 64 | setContentView(R.layout.activity_camera); 65 | 66 | /* opening front facing camera */ 67 | mCamera = openFrontFacingCamera(); 68 | 69 | mProgressScreen = (LinearLayout) findViewById(R.id.preparing); 70 | mCameraPreview = (CameraPreview) findViewById(R.id.camera_surface); 71 | mCameraPreview.setCamera(mCamera); 72 | mCameraLighting = (ImageView) findViewById(R.id.camera_lighting); 73 | mCameraLightingMask = (ImageView) findViewById(R.id.camera_lighting_mask); 74 | mTriggerPicturesButton = (Button) findViewById(R.id.trigger_images_button); 75 | mTriggerPicturesButton.setOnClickListener(new View.OnClickListener() { 76 | 77 | @Override 78 | public void onClick(View view) { 79 | mImgCounter = 0; 80 | mDirName = Storage.getRandomName(10); 81 | setupDisplayScreen(); 82 | takePicture(); 83 | } 84 | }); 85 | 86 | /* prepare the different light sources */ 87 | new PrepareLightSources().execute(getDisplayScreenSize()); 88 | } 89 | 90 | @Override 91 | protected void onPause() { 92 | super.onPause(); 93 | releaseCamera(); 94 | } 95 | 96 | private void takePicture() { 97 | mCameraLighting.setImageDrawable(mLightSourcesList.get(mImgCounter)); 98 | /* give the light source view a little time to update itself */ 99 | new Handler().postDelayed(new Runnable() { 100 | @Override 101 | public void run() { 102 | mCamera.takePicture(null, null, cameraImageCallback()); 103 | } 104 | }, 25); 105 | } 106 | 107 | private PictureCallback cameraImageCallback() { 108 | return new PictureCallback() { 109 | 110 | @Override 111 | public void onPictureTaken(byte[] bytes, Camera camera) { 112 | Bitmap bmp = CameraUtils.fixRotateMirrorImage( 113 | BitmapFactory.decodeByteArray(bytes, 0, bytes.length)); 114 | /* rotate camera image according to camera rotation (portrait vs. 115 | * landscape) */ 116 | Matrix matrix = new Matrix(); 117 | /* compensate the mirror */ 118 | matrix.postRotate((360 - mCameraRotation) % 360); 119 | bmp = Bitmap.createBitmap( 120 | bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); 121 | 122 | String fileName = 123 | Constants.IMAGE_NAME + mImgCounter + "." + Constants.IMAGE_FORMAT; 124 | BitmapUtils.saveBitmap( 125 | BitmapUtils.convertToGrayscale(bmp), mDirName, fileName); 126 | mImgCounter += 1; 127 | mCamera.startPreview(); 128 | if (mImgCounter <= Constants.NUM_IMAGES) { 129 | takePicture(); 130 | } else { 131 | /* start 3d reconstruction asynchronously in background */ 132 | Intent photometricStereo = 133 | new Intent(getApplicationContext(), ReconstructionService.class); 134 | photometricStereo.putExtra( 135 | ReconstructionService.DIRECTORY_NAME, mDirName); 136 | startService(photometricStereo); 137 | /* move to 3d viewer already */ 138 | Intent view3DModel = new Intent( 139 | getApplicationContext(), ReconstructionListActivity.class); 140 | view3DModel.putExtra(RECONSTRUCTION, true); 141 | startActivity(view3DModel); 142 | finish(); 143 | } 144 | } 145 | }; 146 | } 147 | 148 | private void setupDisplayScreen() { 149 | /* hide camera preview */ 150 | LayoutParams layoutParams = mCameraPreview.getLayoutParams(); 151 | layoutParams.width = 0; 152 | layoutParams.height = 0; 153 | mCameraPreview.setLayoutParams(layoutParams); 154 | mTriggerPicturesButton.setVisibility( 155 | View.INVISIBLE); /* hide camera trigger button */ 156 | mCameraLightingMask.setVisibility(View.INVISIBLE); /* hide lighting mask */ 157 | mCameraLighting.setVisibility(View.VISIBLE); /* show light sources on screen */ 158 | } 159 | 160 | private Camera openFrontFacingCamera() { 161 | Camera camera; 162 | int camId = CameraInfo.CAMERA_FACING_FRONT; 163 | try { 164 | camera = Camera.open(camId); 165 | } catch (RuntimeException ex) { 166 | /* no front camera found, trying the first one found */ 167 | camId = 0; 168 | camera = Camera.open(camId); 169 | } 170 | 171 | /* determine current rotation of device */ 172 | mCameraRotation = getDisplayRotation(); 173 | CameraInfo info = new CameraInfo(); 174 | Camera.getCameraInfo(camId, info); 175 | 176 | /* set front facing camera to portrait mode */ 177 | int result = (info.orientation + mCameraRotation) % 360; 178 | /* compensate the mirror */ 179 | result = (360 - result) % 360; 180 | camera.setDisplayOrientation(result); 181 | Camera.Parameters params = camera.getParameters(); 182 | 183 | /* set camera picture size to preferred image resolution */ 184 | Camera.Size targetSize = 185 | CameraUtils.determineTargetPictureSize(params, Constants.IMAGE_RESOLUTION); 186 | params.setPictureSize(targetSize.width, targetSize.height); 187 | camera.setParameters(params); 188 | 189 | return camera; 190 | } 191 | 192 | private int getDisplayRotation() { 193 | switch (getWindowManager().getDefaultDisplay().getRotation()) { 194 | case Surface.ROTATION_0: 195 | return 0; 196 | case Surface.ROTATION_90: 197 | return 90; 198 | case Surface.ROTATION_180: 199 | return 180; 200 | case Surface.ROTATION_270: 201 | return 270; 202 | } 203 | 204 | return 0; 205 | } 206 | 207 | private Size getDisplayScreenSize() { 208 | Display display = getWindowManager().getDefaultDisplay(); 209 | Point size = new Point(); 210 | display.getSize(size); 211 | return new Size(size.x, size.y); 212 | } 213 | 214 | private void releaseCamera() { 215 | if (mCamera != null) { 216 | mCameraPreview.setCamera(null); 217 | mCamera.release(); 218 | mCamera = null; 219 | } 220 | } 221 | 222 | private class PrepareLightSources extends AsyncTask { 223 | private NinePatchDrawable getCamLighting( 224 | Resources res, Size size, int drawableId) { 225 | NinePatchDrawable npd = (NinePatchDrawable) res.getDrawable(drawableId); 226 | if (npd != null) { 227 | npd.setBounds(0, 0, size.width, size.height); 228 | } 229 | return npd; 230 | } 231 | 232 | @Override 233 | protected Void doInBackground(Size... sizes) { 234 | mLightSourcesList = new ArrayList(); 235 | 236 | Resources res = getResources(); 237 | mLightSourcesList.add( 238 | getCamLighting(res, sizes[0], R.drawable.camera_lighting_black)); 239 | mLightSourcesList.add( 240 | getCamLighting(res, sizes[0], R.drawable.camera_lighting_left)); 241 | mLightSourcesList.add( 242 | getCamLighting(res, sizes[0], R.drawable.camera_lighting_top)); 243 | mLightSourcesList.add( 244 | getCamLighting(res, sizes[0], R.drawable.camera_lighting_right)); 245 | mLightSourcesList.add( 246 | getCamLighting(res, sizes[0], R.drawable.camera_lighting_bottom)); 247 | 248 | return null; 249 | } 250 | 251 | @Override 252 | protected void onPostExecute(Void aVoid) { 253 | mProgressScreen.setVisibility(View.INVISIBLE); 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities; 7 | 8 | import android.app.Activity; 9 | import android.content.Intent; 10 | import android.os.Bundle; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | 15 | import de.hsrm.objectify.R; 16 | 17 | public class MainActivity extends Activity { 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | } 23 | 24 | @Override 25 | public boolean onCreateOptionsMenu(Menu menu) { 26 | /* inflate the menu; this adds items to the action bar, if it is present */ 27 | getMenuInflater().inflate(R.menu.menu_main, menu); 28 | return true; 29 | } 30 | 31 | @Override 32 | public boolean onOptionsItemSelected(MenuItem item) { 33 | switch (item.getItemId()) { 34 | case R.id.action_settings: 35 | return true; 36 | case R.id.action_about: 37 | return true; 38 | } 39 | return super.onOptionsItemSelected(item); 40 | } 41 | 42 | public void galleryButtonClick(View target) { 43 | Intent gallActivity = 44 | new Intent(getApplicationContext(), ReconstructionListActivity.class); 45 | startActivity(gallActivity); 46 | } 47 | 48 | public void scanButtonClick(View target) { 49 | Intent camActivity = new Intent(getApplicationContext(), CameraActivity.class); 50 | startActivity(camActivity); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/ReconstructionDetailActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities; 7 | 8 | import android.app.ActionBar; 9 | import android.app.Activity; 10 | import android.app.Fragment; 11 | import android.app.FragmentTransaction; 12 | import android.content.BroadcastReceiver; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.content.IntentFilter; 16 | import android.net.Uri; 17 | import android.os.Bundle; 18 | import android.support.v4.app.NavUtils; 19 | import android.view.Menu; 20 | import android.view.MenuItem; 21 | import android.view.View; 22 | import android.widget.LinearLayout; 23 | import android.widget.SimpleAdapter; 24 | import android.widget.SpinnerAdapter; 25 | 26 | import java.util.ArrayList; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | import de.hsrm.objectify.R; 32 | import de.hsrm.objectify.activities.fragments.ImageViewerFragment; 33 | import de.hsrm.objectify.activities.fragments.ModelViewerFragment; 34 | import de.hsrm.objectify.camera.Constants; 35 | import de.hsrm.objectify.rendering.ReconstructionService; 36 | 37 | /** 38 | * An activity representing a single Reconstruction detail screen. This activity is only 39 | * used on handset devices. On tablet-size devices, item details are presented 40 | * side-by-side with a 41 | * list of items in a {@link ReconstructionListActivity}. This activity is mostly just a 42 | * 'shell' 43 | * activity containing no more than a {@link 44 | * de.hsrm.objectify.activities.fragments.ImageViewerFragment} 45 | */ 46 | public class ReconstructionDetailActivity 47 | extends Activity implements ImageViewerFragment.OnFragmentInteractionListener, 48 | ModelViewerFragment.OnFragmentInteractionListener { 49 | public static final String REC_NORMALMAP = "normalmap"; 50 | public static final String REC_HEIGHTMAP = "heightmap"; 51 | public static final String REC_3DMODEL = "3dmodel"; 52 | private final String TAG = "ReconstructionDetailActivity"; 53 | private LinearLayout mProgressScreen; 54 | private Fragment mCurrentFragment; 55 | private String mGalleryId; 56 | private BroadcastReceiver receiver = new BroadcastReceiver() { 57 | 58 | @Override 59 | public void onReceive(Context context, Intent intent) { 60 | Bundle bundle = intent.getExtras(); 61 | if (bundle != null) { 62 | disableProgressScreen(); 63 | mGalleryId = bundle.getString(ReconstructionService.GALLERY_ID); 64 | mCurrentFragment = 65 | ImageViewerFragment.newInstance(mGalleryId, REC_NORMALMAP); 66 | updateCurrentViewFragment(); 67 | } 68 | } 69 | }; 70 | 71 | @Override 72 | protected void onCreate(Bundle savedInstanceState) { 73 | super.onCreate(savedInstanceState); 74 | setContentView(R.layout.activity_reconstruction_detail); 75 | 76 | /* populate android actionbar dropdown list for all fragments */ 77 | List> data = new ArrayList>(); 78 | String[] titles = getResources().getStringArray(R.array.reconstruction_views); 79 | String[] subs = getResources().getStringArray(R.array.reconstruction_views_sub); 80 | for (int i = 0; i < titles.length; i++) { 81 | Map entry = new HashMap(2); 82 | entry.put("title", titles[i]); 83 | entry.put("subtitle", subs[i]); 84 | data.add(entry); 85 | } 86 | 87 | mProgressScreen = (LinearLayout) findViewById(R.id.progress_screen); 88 | SpinnerAdapter spinnerAdapter = new SimpleAdapter(this, data, 89 | R.layout.subtitled_spinner_item, new String[] {"title", "subtitle"}, 90 | new int[] {android.R.id.text1, android.R.id.text2}); 91 | 92 | /* show the Up button in the action bar. */ 93 | ActionBar actionBar = getActionBar(); 94 | actionBar.setDisplayHomeAsUpEnabled(true); 95 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); 96 | actionBar.setListNavigationCallbacks(spinnerAdapter, spinnerNavigationCallback()); 97 | 98 | /* check if called from reconstruction list or if new scan is happening */ 99 | if (getIntent().hasExtra(ImageViewerFragment.ARG_GALLERY_ID)) { 100 | disableProgressScreen(); 101 | mGalleryId = getIntent().getStringExtra(ImageViewerFragment.ARG_GALLERY_ID); 102 | mCurrentFragment = ImageViewerFragment.newInstance(mGalleryId, REC_NORMALMAP); 103 | updateCurrentViewFragment(); 104 | } else { 105 | actionBar.hide(); 106 | } 107 | } 108 | 109 | private void updateCurrentViewFragment() { 110 | FragmentTransaction transaction = getFragmentManager().beginTransaction(); 111 | transaction.replace(R.id.reconstruction_detail_container, mCurrentFragment); 112 | transaction.commit(); 113 | } 114 | 115 | private void disableProgressScreen() { 116 | getActionBar().show(); 117 | mProgressScreen.setVisibility(View.INVISIBLE); 118 | } 119 | 120 | @Override 121 | protected void onSaveInstanceState(Bundle outState) { 122 | super.onSaveInstanceState(outState); 123 | } 124 | 125 | @Override 126 | public void onResume() { 127 | super.onResume(); 128 | registerReceiver(receiver, new IntentFilter(ReconstructionService.NOTIFICATION)); 129 | } 130 | 131 | @Override 132 | public void onPause() { 133 | super.onPause(); 134 | unregisterReceiver(receiver); 135 | } 136 | 137 | private ActionBar.OnNavigationListener spinnerNavigationCallback() { 138 | return new ActionBar.OnNavigationListener() { 139 | @Override 140 | public boolean onNavigationItemSelected(int itemPosition, long itemId) { 141 | switch (itemPosition) { 142 | case Constants.ReconstructionType.NORMALMAP: 143 | mCurrentFragment = 144 | ImageViewerFragment.newInstance(mGalleryId, REC_NORMALMAP); 145 | break; 146 | case Constants.ReconstructionType.HEIGHTMAP: 147 | mCurrentFragment = 148 | ImageViewerFragment.newInstance(mGalleryId, REC_HEIGHTMAP); 149 | break; 150 | case Constants.ReconstructionType.RECONSTRUCTION: 151 | mCurrentFragment = ModelViewerFragment.newInstance(mGalleryId); 152 | break; 153 | } 154 | 155 | updateCurrentViewFragment(); 156 | return true; 157 | } 158 | }; 159 | } 160 | 161 | @Override 162 | public boolean onCreateOptionsMenu(Menu menu) { 163 | /* inflate the menu; this adds items to the action bar, if it is present */ 164 | getMenuInflater().inflate(R.menu.reconstruction, menu); 165 | return true; 166 | } 167 | 168 | @Override 169 | public boolean onOptionsItemSelected(MenuItem item) { 170 | switch (item.getItemId()) { 171 | case R.id.action_settings: 172 | return true; 173 | case R.id.action_about: 174 | return true; 175 | case android.R.id.home: 176 | /* this ID represents the Home or Up button. In the case of this activity, 177 | * the Up button is shown. Use NavUtils to allow users to navigate up one 178 | * level 179 | * in the application structure */ 180 | NavUtils.navigateUpTo( 181 | this, new Intent(this, ReconstructionListActivity.class)); 182 | return true; 183 | } 184 | return super.onOptionsItemSelected(item); 185 | } 186 | 187 | @Override 188 | public void onFragmentInteraction(Uri uri) {} 189 | } 190 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/ReconstructionListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities; 7 | 8 | import android.app.Activity; 9 | import android.content.Intent; 10 | import android.os.Bundle; 11 | 12 | import de.hsrm.objectify.R; 13 | import de.hsrm.objectify.activities.fragments.ImageViewerFragment; 14 | import de.hsrm.objectify.activities.fragments.ReconstructionListFragment; 15 | 16 | /** 17 | * An activity representing a list of Reconstructions. This activity has different 18 | * presentations for handset and tablet-size devices. On handsets, the activity presents a 19 | * list of items, which when touched, lead to a {@link ReconstructionDetailActivity} 20 | * representing item details. On tablets, the activity presents the list of items and item 21 | * details side-by-side using two vertical panes. 22 | * The activity makes heavy use of fragments. The list of items is a 23 | * {@link ReconstructionListFragment} and the item details (if present) is a 24 | * {@link de.hsrm.objectify.activities.fragments.ImageViewerFragment}. This activity also 25 | * implements the required {@link 26 | * de.hsrm.objectify.activities.fragments.ReconstructionListFragment.Callbacks} 27 | * interface to listen for item selections. 28 | */ 29 | public class ReconstructionListActivity 30 | extends Activity implements ReconstructionListFragment.Callbacks { 31 | /** 32 | * Whether or not the activity is in two-pane mode, i.e. running on a tablet device. 33 | */ 34 | private boolean mTwoPane; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_reconstruction_list); 40 | 41 | if (getIntent().getBooleanExtra(CameraActivity.RECONSTRUCTION, false)) { 42 | /** We are on a small-screen layout and a new reconstruction has been started. 43 | * Moving 44 | * to reconstruction detail activity. */ 45 | Intent view3DModel = 46 | new Intent(getApplicationContext(), ReconstructionDetailActivity.class); 47 | startActivity(view3DModel); 48 | finish(); 49 | } else if (findViewById(R.id.reconstruction_detail_container) != null) { 50 | /** The detail container view will be present only in the large-screen layouts 51 | * (res/values-large and res/values-sw600dp). If this view is present, then 52 | * the activity 53 | * should be in two-pane mode. */ 54 | mTwoPane = true; 55 | 56 | /* In two-pane mode, list items should be given the 'activated' state when 57 | * touched. */ 58 | ((ReconstructionListFragment) getFragmentManager().findFragmentById( 59 | R.id.reconstruction_list)) 60 | .setActivateOnItemClick(true); 61 | } 62 | } 63 | 64 | /** 65 | * Callback method from {@link ReconstructionListFragment.Callbacks} indicating that 66 | * the item 67 | * with the given ID was selected. 68 | */ 69 | @Override 70 | public void onItemSelected(String id) { 71 | if (mTwoPane) { 72 | /** In two-pane mode, show the detail view in this activity by adding or 73 | * replacing the 74 | * detail fragment using a fragment transaction. */ 75 | ImageViewerFragment fragment = ImageViewerFragment.newInstance( 76 | id, ReconstructionDetailActivity.REC_NORMALMAP); 77 | getFragmentManager() 78 | .beginTransaction() 79 | .replace(R.id.reconstruction_detail_container, fragment) 80 | .commit(); 81 | 82 | } else { 83 | /** In single-pane mode, simply start the detail activity for the selected 84 | * item ID. */ 85 | Intent detailIntent = new Intent(this, ReconstructionDetailActivity.class); 86 | detailIntent.putExtra(ImageViewerFragment.ARG_GALLERY_ID, id); 87 | startActivity(detailIntent); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/adapter/ReconstructionListAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities.adapter; 7 | 8 | import android.content.Context; 9 | import android.database.Cursor; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.CursorAdapter; 13 | import android.widget.TextView; 14 | 15 | import de.hsrm.objectify.database.DatabaseAdapter; 16 | 17 | public class ReconstructionListAdapter extends CursorAdapter { 18 | public ReconstructionListAdapter(Context context, Cursor cursor) { 19 | super(context, cursor); 20 | } 21 | 22 | @Override 23 | public View newView(Context context, Cursor cursor, ViewGroup viewGroup) { 24 | return new TextView(context); 25 | } 26 | 27 | @Override 28 | public void bindView(View view, Context context, Cursor cursor) { 29 | TextView textView = (TextView) view; 30 | String imgPath = cursor.getString(DatabaseAdapter.GALLERY_IMAGE_PATH_COLUMN); 31 | textView.setText(imgPath); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/fragments/ImageViewerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities.fragments; 7 | 8 | import android.app.Fragment; 9 | import android.content.ContentResolver; 10 | import android.database.Cursor; 11 | import android.net.Uri; 12 | import android.os.Bundle; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.ImageView; 17 | 18 | import de.hsrm.objectify.R; 19 | import de.hsrm.objectify.activities.ReconstructionDetailActivity; 20 | import de.hsrm.objectify.database.DatabaseAdapter; 21 | import de.hsrm.objectify.database.DatabaseProvider; 22 | import de.hsrm.objectify.rendering.ReconstructionService; 23 | import de.hsrm.objectify.utils.BitmapUtils; 24 | import de.hsrm.objectify.utils.Storage; 25 | 26 | /** 27 | * A fragment representing a single Reconstruction detail screen. 28 | * This fragment is either contained in a {@link 29 | * de.hsrm.objectify.activities.ReconstructionListActivity} 30 | * in two-pane mode (on tablets) or a {@link 31 | * de.hsrm.objectify.activities.ReconstructionDetailActivity} 32 | * on handsets. 33 | */ 34 | public class ImageViewerFragment extends Fragment { 35 | public static final String ARG_GALLERY_ID = "gallery_id"; 36 | public static final String ARG_IMAGE_TYPE = "image_type"; 37 | private String mGalleryId; 38 | private String mImagePath; 39 | 40 | /** 41 | * Mandatory empty constructor for the fragment manager to instantiate the fragment 42 | * (e.g. upon screen orientation changes) 43 | */ 44 | public ImageViewerFragment() {} 45 | 46 | /** 47 | * Use this factory method to create a new instance of this fragment using the 48 | * provided 49 | * parameters. 50 | * 51 | * @param galleryId gallery database id 52 | * @return A new instance of fragment ModelViewerFragment. 53 | */ 54 | public static ImageViewerFragment newInstance(String galleryId, String recType) { 55 | ImageViewerFragment fragment = new ImageViewerFragment(); 56 | Bundle args = new Bundle(); 57 | args.putString(ARG_GALLERY_ID, galleryId); 58 | args.putString(ARG_IMAGE_TYPE, recType); 59 | fragment.setArguments(args); 60 | return fragment; 61 | } 62 | 63 | @Override 64 | public void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | 67 | if (getArguments() != null) { 68 | mGalleryId = getArguments().getString(ARG_GALLERY_ID); 69 | String imageType = getArguments().getString(ARG_IMAGE_TYPE); 70 | String dirPath = getDirectoryPathFromDatabase(mGalleryId); 71 | mImagePath = getReconstructionTypeImagePath(dirPath, imageType); 72 | } 73 | } 74 | 75 | private String getReconstructionTypeImagePath(String dirPath, String recType) { 76 | if (recType.equalsIgnoreCase(ReconstructionDetailActivity.REC_NORMALMAP)) { 77 | return Storage.getExternalRootDirectory() + "/" + dirPath + "/" 78 | + ReconstructionService.NORMAL_IMG_NAME; 79 | } else if (recType.equalsIgnoreCase(ReconstructionDetailActivity.REC_HEIGHTMAP)) { 80 | return Storage.getExternalRootDirectory() + "/" + dirPath + "/" 81 | + ReconstructionService.HEIGHT_IMG_NAME; 82 | } 83 | 84 | return null; 85 | } 86 | 87 | private String getDirectoryPathFromDatabase(String galleryId) { 88 | ContentResolver cr = getActivity().getContentResolver(); 89 | Uri galleryItemUri = DatabaseProvider.CONTENT_URI.buildUpon() 90 | .appendPath(DatabaseAdapter.DATABASE_TABLE_GALLERY) 91 | .build(); 92 | Cursor c = cr.query(galleryItemUri, null, DatabaseAdapter.GALLERY_ID_KEY + "=?", 93 | new String[]{mGalleryId}, null); 94 | String imgFilePath = null; 95 | if (c != null) { 96 | c.moveToFirst(); 97 | imgFilePath = c.getString(DatabaseAdapter.GALLERY_IMAGE_PATH_COLUMN); 98 | c.close(); 99 | } 100 | 101 | return imgFilePath; 102 | } 103 | 104 | @Override 105 | public View onCreateView( 106 | LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 107 | View rootView = 108 | inflater.inflate(R.layout.fragment_normal_map_view, container, false); 109 | ImageView reconstructionImageView = 110 | (ImageView) rootView.findViewById(R.id.reconstruction_image); 111 | reconstructionImageView.setImageBitmap(BitmapUtils.openBitmap(mImagePath)); 112 | return rootView; 113 | } 114 | 115 | /** 116 | * This interface must be implemented by activities that contain this fragment to 117 | * allow an 118 | * interaction in this fragment to be communicated to the activity and potentially 119 | * other 120 | * fragments contained in that activity. 121 | */ 122 | public interface OnFragmentInteractionListener { 123 | // TODO: Update argument type and name 124 | void onFragmentInteraction(Uri uri); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/fragments/ModelViewerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities.fragments; 7 | 8 | import android.app.Fragment; 9 | import android.content.ContentResolver; 10 | import android.database.Cursor; 11 | import android.graphics.Bitmap; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Point; 14 | import android.net.Uri; 15 | import android.os.Bundle; 16 | import android.util.Log; 17 | import android.view.Display; 18 | import android.view.LayoutInflater; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.FrameLayout; 22 | 23 | import java.io.FileInputStream; 24 | import java.io.IOException; 25 | import java.io.ObjectInputStream; 26 | 27 | import de.hsrm.objectify.R; 28 | import de.hsrm.objectify.database.DatabaseAdapter; 29 | import de.hsrm.objectify.database.DatabaseProvider; 30 | import de.hsrm.objectify.rendering.ObjectModel; 31 | import de.hsrm.objectify.rendering.TouchSurfaceView; 32 | import de.hsrm.objectify.utils.Storage; 33 | 34 | /** 35 | * A simple {@link Fragment} subclass. Activities that contain this fragment must 36 | * implement the 37 | * {@link ModelViewerFragment.OnFragmentInteractionListener} interface to handle 38 | * interactions 39 | */ 40 | public class ModelViewerFragment extends Fragment { 41 | private static final String ARG_GALLERY_ID = "gallery_id"; 42 | private ObjectModel mObjectModel; 43 | private String mGalleryId; 44 | 45 | /** 46 | * Mandatory empty constructor for the fragment manager to instantiate the fragment 47 | * (e.g. upon 48 | * screen orientation changes) 49 | */ 50 | public ModelViewerFragment() {} 51 | 52 | /** 53 | * Use this factory method to create a new instance of this fragment using the 54 | * provided 55 | * parameters. 56 | * 57 | * @param galleryId gallery database id 58 | * @return A new instance of fragment ModelViewerFragment. 59 | */ 60 | // TODO: Rename and change types and number of parameters 61 | public static ModelViewerFragment newInstance(String galleryId) { 62 | ModelViewerFragment fragment = new ModelViewerFragment(); 63 | Bundle args = new Bundle(); 64 | args.putString(ARG_GALLERY_ID, galleryId); 65 | fragment.setArguments(args); 66 | return fragment; 67 | } 68 | 69 | @Override 70 | public void onCreate(Bundle savedInstanceState) { 71 | super.onCreate(savedInstanceState); 72 | if (savedInstanceState != null) { 73 | mGalleryId = savedInstanceState.getString(ARG_GALLERY_ID); 74 | } else if (getArguments().getString(ARG_GALLERY_ID) != null) { 75 | mGalleryId = getArguments().getString(ARG_GALLERY_ID); 76 | Log.i("ModelViewerFragment", "galleryID: " + mGalleryId); 77 | String path = getModelPathFromDatabase(mGalleryId); 78 | /* TODO: load object asynchronously */ 79 | try { 80 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path)); 81 | mObjectModel = (ObjectModel) ois.readObject(); 82 | ois.close(); 83 | mObjectModel.setTextureBitmap(getTextureFromDatabase(mGalleryId)); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | Log.e("ModelViewerFragment", "Could not read objectmodel file"); 87 | } catch (ClassNotFoundException e) { 88 | e.printStackTrace(); 89 | Log.e("ModelViewerFragment", "Could not cast to ObjectModel"); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public void onSaveInstanceState(Bundle outState) { 96 | outState.putString(ARG_GALLERY_ID, mGalleryId); 97 | super.onSaveInstanceState(outState); 98 | } 99 | 100 | @Override 101 | public View onCreateView( 102 | LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 103 | /* view container, in this case FrameLayout */ 104 | View rootView = 105 | inflater.inflate(R.layout.fragment_model_viewer, container, false); 106 | FrameLayout frameLayout = 107 | (FrameLayout) rootView.findViewById(R.id.modelview_container); 108 | 109 | /* add touchsurface view, for displaying object model */ 110 | Display d = getActivity().getWindowManager().getDefaultDisplay(); 111 | Point size = new Point(); 112 | d.getSize(size); 113 | TouchSurfaceView glSurfaceView = 114 | new TouchSurfaceView(getActivity(), size.x, size.y); 115 | frameLayout.addView(glSurfaceView); 116 | glSurfaceView.setObjectModel(mObjectModel); 117 | 118 | return rootView; 119 | } 120 | 121 | private Bitmap getTextureFromDatabase(String galleryId) { 122 | ContentResolver cr = getActivity().getContentResolver(); 123 | Uri galleryItemUri = DatabaseProvider.CONTENT_URI.buildUpon() 124 | .appendPath(DatabaseAdapter.DATABASE_TABLE_GALLERY) 125 | .build(); 126 | Cursor c = cr.query(galleryItemUri, null, DatabaseAdapter.GALLERY_ID_KEY + "=?", 127 | new String[] {mGalleryId}, null); 128 | c.moveToFirst(); 129 | Bitmap texture = BitmapFactory.decodeFile(Storage.getExternalRootDirectory() + "/" 130 | + c.getString(DatabaseAdapter.GALLERY_IMAGE_PATH_COLUMN) + "/image_1.png"); 131 | c.close(); 132 | return texture; 133 | } 134 | 135 | private String getModelPathFromDatabase(String galleryId) { 136 | ContentResolver cr = getActivity().getContentResolver(); 137 | Uri galleryItemUri = DatabaseProvider.CONTENT_URI.buildUpon() 138 | .appendPath(DatabaseAdapter.DATABASE_TABLE_GALLERY) 139 | .build(); 140 | Cursor c = cr.query(galleryItemUri, null, DatabaseAdapter.GALLERY_ID_KEY + "=?", 141 | new String[] {mGalleryId}, null); 142 | c.moveToFirst(); 143 | String objectId = c.getString(DatabaseAdapter.GALLERY_OBJECT_ID_COLUMN); 144 | c.close(); 145 | 146 | Uri objectItemUri = DatabaseProvider.CONTENT_URI.buildUpon() 147 | .appendPath(DatabaseAdapter.DATABASE_TABLE_OBJECT) 148 | .build(); 149 | c = cr.query(objectItemUri, null, DatabaseAdapter.OBJECT_ID_KEY + "=?", 150 | new String[] {objectId}, null); 151 | c.moveToFirst(); 152 | String objFilePath = c.getString(DatabaseAdapter.OBJECT_FILE_PATH_COLUMN); 153 | c.close(); 154 | 155 | return objFilePath; 156 | } 157 | 158 | /** 159 | * This interface must be implemented by activities that contain this fragment to 160 | * allow an 161 | * interaction in this fragment to be communicated to the activity and potentially 162 | * other 163 | * fragments contained in that activity. 164 | */ 165 | public interface OnFragmentInteractionListener { 166 | // TODO: Update argument type and name 167 | void onFragmentInteraction(Uri uri); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/activities/fragments/ReconstructionListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.activities.fragments; 7 | 8 | import android.app.Activity; 9 | import android.app.ListFragment; 10 | import android.database.Cursor; 11 | import android.net.Uri; 12 | import android.os.Bundle; 13 | import android.view.View; 14 | import android.widget.ListView; 15 | 16 | import de.hsrm.objectify.activities.adapter.ReconstructionListAdapter; 17 | import de.hsrm.objectify.database.DatabaseAdapter; 18 | import de.hsrm.objectify.database.DatabaseProvider; 19 | 20 | /** 21 | * A list fragment representing a list of Reconstructions. This fragment also supports 22 | * tablet devices by allowing list items to be given an 'activated' state upon selection. 23 | * This helps indicate which item is currently being viewed in a {@link 24 | * ImageViewerFragment}. 25 | * Activities containing this fragment MUST implement the {@link Callbacks} interface 26 | */ 27 | public class ReconstructionListFragment extends ListFragment { 28 | private static final String STATE_ACTIVATED_POSITION = "activated_position"; 29 | 30 | /** 31 | * The fragment's current callback object, which is notified of list item clicks 32 | */ 33 | private Callbacks mCallbacks; 34 | 35 | /** 36 | * The current activated item position. Only used on tablets 37 | */ 38 | private int mActivatedPosition = ListView.INVALID_POSITION; 39 | 40 | /** 41 | * Mandatory empty constructor for the fragment manager to instantiate the fragment 42 | * (e.g. upon 43 | * screen orientation changes) 44 | */ 45 | public ReconstructionListFragment() {} 46 | 47 | @Override 48 | public void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | 51 | Uri galleryUri = DatabaseProvider.CONTENT_URI.buildUpon() 52 | .appendPath(DatabaseAdapter.DATABASE_TABLE_GALLERY) 53 | .build(); 54 | Cursor cursor = getActivity().managedQuery(galleryUri, null, null, null, null); 55 | ReconstructionListAdapter adapter = 56 | new ReconstructionListAdapter(getActivity().getApplicationContext(), cursor); 57 | setListAdapter(adapter); 58 | } 59 | 60 | @Override 61 | public void onViewCreated(View view, Bundle savedInstance) { 62 | super.onViewCreated(view, savedInstance); 63 | 64 | /* restore the previously serialized activated item position */ 65 | if (savedInstance != null 66 | && savedInstance.containsKey(STATE_ACTIVATED_POSITION)) { 67 | setActivatedPosition(savedInstance.getInt(STATE_ACTIVATED_POSITION)); 68 | } 69 | } 70 | 71 | @Override 72 | public void onAttach(Activity activity) { 73 | super.onAttach(activity); 74 | 75 | /* activities containing this fragment must implement its callbacks */ 76 | if (!(activity instanceof Callbacks)) { 77 | throw new IllegalStateException( 78 | "Activity must implement fragment's callbacks."); 79 | } 80 | 81 | mCallbacks = (Callbacks) activity; 82 | } 83 | 84 | @Override 85 | public void onListItemClick(ListView listView, View view, int position, long id) { 86 | super.onListItemClick(listView, view, position, id); 87 | 88 | /* notify the active callbacks interface (the activity, if the fragment is 89 | * attached to one) 90 | * that an item has been selected */ 91 | mCallbacks.onItemSelected(String.valueOf(id)); 92 | } 93 | 94 | @Override 95 | public void onSaveInstanceState(Bundle outState) { 96 | super.onSaveInstanceState(outState); 97 | if (mActivatedPosition != ListView.INVALID_POSITION) { 98 | /* serialize and persist the activated item position */ 99 | outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); 100 | } 101 | } 102 | 103 | /** 104 | * Turns on activate-on-click mode. When this mode is on, list items will be given the 105 | * 'activated' state when touched 106 | */ 107 | public void setActivateOnItemClick(boolean activateOnItemClick) { 108 | /* when setting CHOICE_MODE_SINGLE, ListView will automatically give items the 109 | * 'activated' 110 | * state when touched */ 111 | getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE 112 | : ListView.CHOICE_MODE_NONE); 113 | } 114 | 115 | private void setActivatedPosition(int position) { 116 | if (position == ListView.INVALID_POSITION) { 117 | getListView().setItemChecked(mActivatedPosition, false); 118 | } else { 119 | getListView().setItemChecked(position, true); 120 | } 121 | 122 | mActivatedPosition = position; 123 | } 124 | 125 | /** 126 | * A callback interface that all activities containing this fragment must implement. 127 | * This 128 | * mechanism allows activities to be notified of item selections 129 | */ 130 | public interface Callbacks { void onItemSelected(String id); } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/camera/CameraPreview.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.camera; 7 | 8 | import android.content.Context; 9 | import android.hardware.Camera; 10 | import android.hardware.Camera.Size; 11 | import android.util.AttributeSet; 12 | import android.util.Log; 13 | import android.view.SurfaceHolder; 14 | import android.view.SurfaceView; 15 | 16 | import java.io.IOException; 17 | 18 | import de.hsrm.objectify.utils.CameraUtils; 19 | 20 | public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 21 | private SurfaceHolder mHolder; 22 | private Camera mCamera; 23 | 24 | public CameraPreview(Context context) { 25 | super(context); 26 | initialize(); 27 | } 28 | 29 | public CameraPreview(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | initialize(); 32 | } 33 | 34 | public CameraPreview(Context context, AttributeSet attrs, int defStyle) { 35 | super(context, attrs, defStyle); 36 | initialize(); 37 | } 38 | 39 | private void initialize() { 40 | mHolder = getHolder(); 41 | mHolder.addCallback(this); 42 | mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 43 | } 44 | 45 | public void setCamera(Camera camera) { 46 | if (this.mCamera == camera) { 47 | return; 48 | } 49 | 50 | this.mCamera = camera; 51 | } 52 | 53 | @Override 54 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 55 | if (mCamera == null) { 56 | return; 57 | } 58 | 59 | try { 60 | mCamera.setPreviewDisplay(mHolder); 61 | } catch (IOException ex) { 62 | String TAG = "CameraPreview"; 63 | Log.e(TAG, ex.getLocalizedMessage()); 64 | mCamera.release(); 65 | mCamera = null; 66 | } 67 | } 68 | 69 | @Override 70 | public void surfaceChanged( 71 | SurfaceHolder surfaceHolder, int format, int width, int height) { 72 | if (mCamera == null) { 73 | return; 74 | } 75 | 76 | Camera.Parameters params = mCamera.getParameters(); 77 | Size targetSize = 78 | CameraUtils.determineTargetPictureSize(params, Constants.IMAGE_RESOLUTION); 79 | params.setPreviewSize(targetSize.width, targetSize.height); 80 | mCamera.setParameters(params); 81 | mCamera.startPreview(); 82 | } 83 | 84 | @Override 85 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 86 | if (mCamera == null) { 87 | return; 88 | } 89 | 90 | mCamera.stopPreview(); 91 | mCamera.release(); 92 | mCamera = null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/camera/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.camera; 7 | 8 | import android.graphics.Bitmap; 9 | 10 | public interface Constants { 11 | int IMAGE_RESOLUTION = 120 * 160; 12 | int NUM_IMAGES = 4; 13 | String IMAGE_NAME = "image_"; 14 | String IMAGE_FORMAT = "png"; 15 | Bitmap.CompressFormat IMAGE_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG; 16 | 17 | class ReconstructionType { 18 | public static final int NORMALMAP = 0; 19 | public static final int HEIGHTMAP = 1; 20 | public static final int RECONSTRUCTION = 2; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/database/DatabaseAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.database; 7 | 8 | import android.content.Context; 9 | import android.database.sqlite.SQLiteDatabase; 10 | import android.database.sqlite.SQLiteException; 11 | import android.database.sqlite.SQLiteOpenHelper; 12 | 13 | /** 14 | * @brief Database adapter responsible for creation of db tables and updating db 15 | *

16 | * This class manages several database tables which look like following: 17 | * clang-format off 18 | * @code 19 | * +-----------------+ +--------------------------------------------------------------------+ 20 | * | object | | gallery | 21 | * +-----+-----------+ +-----+------------+------+-----------+-------+----------+-----------+ 22 | * | _id | file_path | | _id | image_path | date | dimension | faces | vertices | object_id | 23 | * +-----+-----------+ +-----+------------+------+-----------+-------+----------+-----------+ 24 | * @endcode 25 | * clang-format on 26 | */ 27 | public class DatabaseAdapter { 28 | 29 | public static final String DATABASE_TABLE_OBJECT = "object"; 30 | public static final String OBJECT_ID_KEY = "_id"; 31 | public static final int OBJECT_ID_COLUMN = 0; 32 | public static final String OBJECT_FILE_PATH_KEY = "file_path"; 33 | private static final String DATABASE_TABLE_OBJECT_CREATE = "CREATE TABLE " 34 | + DATABASE_TABLE_OBJECT 35 | + " (" 36 | + OBJECT_ID_KEY + " INTEGER PRIMARY KEY AUTOINCREMENT, " 37 | + OBJECT_FILE_PATH_KEY + " TEXT NOT NULL" 38 | + ")"; 39 | public static final int OBJECT_FILE_PATH_COLUMN = 1; 40 | public static final String DATABASE_TABLE_GALLERY = "gallery"; 41 | public static final String GALLERY_ID_KEY = "_id"; 42 | public static final int GALLERY_ID_COLUMN = 0; 43 | public static final String GALLERY_IMAGE_PATH_KEY = "image_path"; 44 | public static final int GALLERY_IMAGE_PATH_COLUMN = 1; 45 | public static final String GALLERY_DATE_KEY = "date"; 46 | public static final int GALLERY_DATE_COLUMN = 2; 47 | public static final String GALLERY_DIMENSION_KEY = "dimension"; 48 | public static final int GALLERY_DIMENSION_COLUMN = 3; 49 | public static final String GALLERY_FACES_KEY = "faces"; 50 | public static final int GALLERY_FACES_COLUMN = 4; 51 | public static final String GALLERY_VERTICES_KEY = "vertices"; 52 | public static final int GALLERY_VERTICES_COLUMN = 5; 53 | public static final String GALLERY_OBJECT_ID_KEY = "object_id"; 54 | private static final String DATABASE_TABLE_GALLERY_CREATE = "CREATE TABLE " 55 | + DATABASE_TABLE_GALLERY 56 | + " (" 57 | + GALLERY_ID_KEY + " INTEGER PRIMARY KEY AUTOINCREMENT, " 58 | + GALLERY_IMAGE_PATH_KEY + " TEXT NOT NULL, " 59 | + GALLERY_DATE_KEY + " TEXT NOT NULL, " 60 | + GALLERY_DIMENSION_KEY + " TEXT NOT NULL, " 61 | + GALLERY_FACES_KEY + " TEXT NOT NULL, " 62 | + GALLERY_VERTICES_KEY + " TEXT NOT NULL, " 63 | + GALLERY_OBJECT_ID_KEY + " INTEGER REFERENCES " + DATABASE_TABLE_OBJECT 64 | + ")"; 65 | public static final int GALLERY_OBJECT_ID_COLUMN = 6; 66 | private static final String DATABASE_NAME = "objectify.db"; 67 | private static final int DATABASE_VERSION = 1; 68 | private static SQLiteDatabase db; 69 | private DatabaseHelper dbHelper; 70 | 71 | public DatabaseAdapter(Context context) { 72 | dbHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); 73 | } 74 | 75 | public void open() throws SQLiteException { 76 | try { 77 | db = dbHelper.getWritableDatabase(); 78 | } catch (SQLiteException e) { 79 | db = dbHelper.getReadableDatabase(); 80 | } 81 | } 82 | 83 | public void close() { 84 | db.close(); 85 | } 86 | 87 | public SQLiteDatabase getDatabase() { 88 | return db; 89 | } 90 | 91 | /** 92 | * Helper class for managing {@link android.database.sqlite.SQLiteDatabase} that 93 | * stores data for {@link DatabaseProvider} 94 | */ 95 | private static class DatabaseHelper extends SQLiteOpenHelper { 96 | public DatabaseHelper(Context context, String name, 97 | SQLiteDatabase.CursorFactory factory, int version) { 98 | super(context, name, factory, version); 99 | } 100 | 101 | @Override 102 | public void onCreate(SQLiteDatabase db) { 103 | db.execSQL(DATABASE_TABLE_OBJECT_CREATE); 104 | db.execSQL(DATABASE_TABLE_GALLERY_CREATE); 105 | } 106 | 107 | @Override 108 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 109 | db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_GALLERY); 110 | db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_OBJECT); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/database/DatabaseProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.database; 7 | 8 | import android.content.ContentProvider; 9 | import android.content.ContentValues; 10 | import android.content.UriMatcher; 11 | import android.database.Cursor; 12 | import android.database.sqlite.SQLiteDatabase; 13 | import android.net.Uri; 14 | import android.util.Log; 15 | 16 | public class DatabaseProvider extends ContentProvider { 17 | private static final String AUTHORITY = 18 | "de.hsrm.objectify.database.databaseprovider.content"; 19 | private static final String CONTENT_URI_STRING = "content://" + AUTHORITY; 20 | public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING); 21 | private static final String TAG = "DatabaseProvider"; 22 | private static final int GALLERY = 1; 23 | private static final int OBJECT = 2; 24 | 25 | private static final UriMatcher uriMatcher; 26 | private static final String VND_GALLERY_DIR = 27 | "vnd.android.cursor.dir/vnd.de.android.gallery"; 28 | private static final String VND_GALLERY_ITEM = 29 | "vnd.android.cursor.item/vnd.de.android.gallery"; 30 | private static final String VND_OBJECT_DIR = 31 | "vnd.android.cursor.dir/vnd.de.android.object"; 32 | private static final String VND_OBJECT_ITEM = 33 | "vnd.android.cursor.item/vnd.de.android.object"; 34 | static { 35 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 36 | uriMatcher.addURI(AUTHORITY, "gallery", GALLERY); 37 | uriMatcher.addURI(AUTHORITY, "object", OBJECT); 38 | } 39 | 40 | private SQLiteDatabase db; 41 | 42 | @Override 43 | public boolean onCreate() { 44 | DatabaseAdapter dbadapter = new DatabaseAdapter(getContext()); 45 | dbadapter.open(); 46 | db = dbadapter.getDatabase(); 47 | return true; 48 | } 49 | 50 | @Override 51 | public Cursor query(Uri uri, String[] projection, String selection, 52 | String[] selectionArgs, String sortOrder) { 53 | int code = uriMatcher.match(uri); 54 | switch (code) { 55 | case GALLERY: 56 | return db.query(DatabaseAdapter.DATABASE_TABLE_GALLERY, projection, 57 | selection, selectionArgs, null, null, null); 58 | case OBJECT: 59 | return db.query(DatabaseAdapter.DATABASE_TABLE_OBJECT, projection, 60 | selection, selectionArgs, null, null, null); 61 | default: 62 | Log.e(TAG, "uriMatcher.match(uri) error"); 63 | return null; 64 | } 65 | } 66 | 67 | @Override 68 | public String getType(Uri uri) { 69 | switch (uriMatcher.match(uri)) { 70 | case GALLERY: 71 | return VND_GALLERY_DIR; 72 | case OBJECT: 73 | return VND_OBJECT_DIR; 74 | default: 75 | throw new IllegalArgumentException("Unsupported Uri: " + uri); 76 | } 77 | } 78 | 79 | @Override 80 | public Uri insert(Uri uri, ContentValues contentValues) { 81 | long rowId; 82 | Uri erg; 83 | int code = uriMatcher.match(uri); 84 | switch (code) { 85 | case GALLERY: 86 | rowId = db.insert( 87 | DatabaseAdapter.DATABASE_TABLE_GALLERY, null, contentValues); 88 | erg = (rowId > 0) 89 | ? CONTENT_URI.buildUpon() 90 | .appendPath("gallery") 91 | .appendPath("" + rowId) 92 | .build() 93 | : null; 94 | return erg; 95 | case OBJECT: 96 | rowId = 97 | db.insert(DatabaseAdapter.DATABASE_TABLE_OBJECT, null, contentValues); 98 | erg = (rowId > 0) 99 | ? CONTENT_URI.buildUpon() 100 | .appendPath("object") 101 | .appendPath("" + rowId) 102 | .build() 103 | : null; 104 | return erg; 105 | } 106 | 107 | return null; 108 | } 109 | 110 | @Override 111 | public int update( 112 | Uri uri, ContentValues values, String selection, String[] selectionArgs) { 113 | switch (uriMatcher.match(uri)) { 114 | case GALLERY: 115 | return db.update(DatabaseAdapter.DATABASE_TABLE_GALLERY, values, 116 | selection, selectionArgs); 117 | case OBJECT: 118 | return db.update(DatabaseAdapter.DATABASE_TABLE_OBJECT, values, selection, 119 | selectionArgs); 120 | default: 121 | Log.e(TAG, "uriMatcher.match(uri) error"); 122 | return 0; 123 | } 124 | } 125 | 126 | @Override 127 | public int delete(Uri uri, String selection, String[] selectionArgs) { 128 | int erg; 129 | switch (uriMatcher.match(uri)) { 130 | case GALLERY: 131 | erg = db.delete( 132 | DatabaseAdapter.DATABASE_TABLE_GALLERY, selection, selectionArgs); 133 | return erg; 134 | case OBJECT: 135 | erg = db.delete( 136 | DatabaseAdapter.DATABASE_TABLE_OBJECT, selection, selectionArgs); 137 | return erg; 138 | } 139 | return 0; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/export/OBJExport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.export; 7 | 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | 11 | import java.io.BufferedWriter; 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.FileOutputStream; 14 | import java.io.FileWriter; 15 | import java.io.IOException; 16 | 17 | import de.hsrm.objectify.rendering.ObjectModel; 18 | import de.hsrm.objectify.utils.Storage; 19 | 20 | public class OBJExport { 21 | public static final String OBJ_NAME = "export.obj"; 22 | 23 | public static boolean write(ObjectModel objectModel, Bitmap texture, String dirName) { 24 | String suffix = Storage.getExternalRootDirectory() + "/" + dirName + "/"; 25 | String objPath = suffix + OBJ_NAME; 26 | String mtlPath = suffix + "objectify_model.mtl"; 27 | String texPath = suffix + "objectify_model.jpg"; 28 | 29 | try { 30 | BufferedWriter out = new BufferedWriter(new FileWriter(objPath)); 31 | out.write("# Created by Objectify\n"); 32 | out.write("mtllib objectify_model.mtl\n"); 33 | float[] vertices = objectModel.getVertices(); 34 | short[] faces = objectModel.getFaces(); 35 | 36 | /* write vertices */ 37 | for (int i = 0; i < vertices.length; i += 3) { 38 | out.write("v " + vertices[i] + " " + vertices[i + 1] + " " 39 | + vertices[i + 2] + "\n"); 40 | } 41 | 42 | /* writing texture coords */ 43 | int width = texture.getWidth(); 44 | int height = texture.getHeight(); 45 | for (int h = height - 1; h >= 0; h--) { 46 | for (int w = 0; w < width; w++) { 47 | out.write("vt " + (float) w / (float) (width - 1) + " " 48 | + (float) h / (float) (height - 1) + "\n"); 49 | } 50 | } 51 | /* writing faces */ 52 | out.write("usemtl picture\n"); 53 | for (int i = 0; i < faces.length; i += 3) { 54 | int one = faces[i] + 1; 55 | int two = faces[i + 1] + 1; 56 | int three = faces[i + 2] + 1; 57 | out.write("f " + one + "/" + one + " " + two + "/" + two + " " + three 58 | + "/" + three + "\n"); 59 | } 60 | 61 | out.write("\n"); 62 | out.flush(); 63 | out.close(); 64 | 65 | /* Write texture image to jpg file */ 66 | byte[] bb = objectModel.mBitmapData; 67 | Bitmap textureBitmap = BitmapFactory.decodeByteArray(bb, 0, bb.length); 68 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 69 | textureBitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos); 70 | FileOutputStream fout = new FileOutputStream(texPath); 71 | fout.write(baos.toByteArray()); 72 | 73 | /* Create mtl for texture */ 74 | out = new BufferedWriter(new FileWriter(mtlPath)); 75 | out.write("newmtl picture\n"); 76 | out.write("map_Kd objectify_model.jpg\n"); 77 | out.flush(); 78 | out.close(); 79 | 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | return false; 83 | } 84 | 85 | return true; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/math/Matrix4f.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.math; 7 | 8 | public class Matrix4f { 9 | // __ __ 10 | // | | 11 | // | m11 m12 m13 m14 | 12 | // | m21 m22 m23 m24 | 13 | // | m31 m32 m33 m34 | 14 | // | m41 m42 m43 m44 | 15 | // |__ __| 16 | // 17 | public float _11, _12, _13, _14; 18 | public float _21, _22, _23, _24; 19 | public float _31, _32, _33, _34; 20 | public float _41, _42, _43, _44; 21 | 22 | /** 23 | * Constructs a 0 value matrix. 24 | */ 25 | public Matrix4f() { 26 | // 27 | } 28 | 29 | /** 30 | * Constructs a matrix an another matrix. 31 | */ 32 | public Matrix4f(Matrix4f pmatrix) { 33 | this.copy(pmatrix); 34 | } 35 | 36 | /** 37 | * Returns the determinant of a matrix build by the 4th row. 38 | * 39 | * @param pm The matrix. 40 | */ 41 | public static float determinant(Matrix4f pm) { 42 | 43 | float det; 44 | float det41, det42, det43, det44; 45 | float a41, a42, a43, a44; 46 | 47 | a41 = -pm._41; 48 | a42 = +pm._42; 49 | a43 = -pm._43; 50 | a44 = +pm._44; 51 | // 52 | // build the determinant of the adjunct matrices 53 | // 54 | det41 = ((pm._12 * pm._23 * pm._34) + (pm._13 * pm._24 * pm._32) + (pm._14 55 | * pm._22 * pm._33)) 56 | - ((pm._14 * pm._23 * pm._32) + (pm._12 * pm._24 * pm._33) + (pm._13 57 | * pm._22 * pm._34)); 58 | 59 | det42 = ((pm._11 * pm._23 * pm._34) + (pm._13 * pm._24 * pm._31) + (pm._14 60 | * pm._21 * pm._33)) 61 | - ((pm._14 * pm._23 * pm._31) + (pm._11 * pm._24 * pm._33) + (pm._13 62 | * pm._21 * pm._34)); 63 | 64 | det43 = ((pm._11 * pm._22 * pm._34) + (pm._12 * pm._24 * pm._31) + (pm._14 65 | * pm._21 * pm._32)) 66 | - ((pm._14 * pm._22 * pm._31) + (pm._11 * pm._24 * pm._32) + (pm._12 67 | * pm._21 * pm._34)); 68 | 69 | det44 = ((pm._11 * pm._22 * pm._33) + (pm._12 * pm._23 * pm._31) + (pm._13 70 | * pm._21 * pm._32)) 71 | - ((pm._13 * pm._22 * pm._31) + (pm._11 * pm._23 * pm._32) + (pm._12 72 | * pm._21 * pm._33)); 73 | // 74 | // build the determinant 75 | // 76 | det = (a41 * det41) + (a42 * det42) + (a43 * det43) + (a44 * det44); 77 | return det; 78 | } 79 | 80 | /** 81 | * Create the inverse of a given matrix. Returns true if matrix is 82 | * invertible false if matrix has determinant of 0. If false was returned 83 | * this method has on effect on the destination matrix. 84 | * 85 | * @param pm The matrix which is to invert. 86 | * @param pdest The source matrix reference. 87 | * @return The invertibility of the given matrix. 88 | */ 89 | public static boolean invert(Matrix4f pm, Matrix4f pdest) { 90 | 91 | float det; 92 | float idet; 93 | 94 | float a11, a12, a13, a14; 95 | float a21, a22, a23, a24; 96 | float a31, a32, a33, a34; 97 | float a41, a42, a43, a44; 98 | // 99 | // if determinant of matrix is 0, matrix is not regular 100 | // 101 | det = determinant(pm); 102 | if (det == 0) 103 | return false; 104 | // 105 | // build the reciprocal of the determinant 106 | // 107 | idet = 1.0f / det; 108 | // 109 | // build the adjuncts 110 | // 111 | a11 = ((pm._22 * pm._33 * pm._44) + (pm._23 * pm._34 * pm._42) + (pm._24 112 | * pm._32 * pm._43)) 113 | - ((pm._24 * pm._33 * pm._42) + (pm._22 * pm._34 * pm._43) + (pm._23 114 | * pm._32 * pm._44)); 115 | 116 | a21 = ((pm._21 * pm._33 * pm._44) + (pm._23 * pm._34 * pm._41) + (pm._24 117 | * pm._31 * pm._43)) 118 | - ((pm._24 * pm._33 * pm._41) + (pm._21 * pm._34 * pm._43) + (pm._23 119 | * pm._31 * pm._44)); 120 | 121 | a31 = ((pm._21 * pm._32 * pm._44) + (pm._22 * pm._34 * pm._41) + (pm._24 122 | * pm._31 * pm._42)) 123 | - ((pm._24 * pm._32 * pm._41) + (pm._21 * pm._34 * pm._42) + (pm._22 124 | * pm._31 * pm._44)); 125 | 126 | a41 = ((pm._21 * pm._32 * pm._43) + (pm._22 * pm._33 * pm._41) + (pm._23 127 | * pm._31 * pm._42)) 128 | - ((pm._23 * pm._32 * pm._41) + (pm._21 * pm._33 * pm._42) + (pm._22 129 | * pm._31 * pm._43)); 130 | // 131 | // 132 | a12 = ((pm._12 * pm._33 * pm._44) + (pm._13 * pm._34 * pm._42) + (pm._14 133 | * pm._32 * pm._43)) 134 | - ((pm._14 * pm._33 * pm._42) + (pm._12 * pm._34 * pm._43) + (pm._13 135 | * pm._32 * pm._44)); 136 | 137 | a22 = ((pm._11 * pm._33 * pm._44) + (pm._13 * pm._34 * pm._41) + (pm._14 138 | * pm._31 * pm._43)) 139 | - ((pm._14 * pm._33 * pm._41) + (pm._11 * pm._34 * pm._43) + (pm._13 140 | * pm._31 * pm._44)); 141 | 142 | a32 = ((pm._11 * pm._32 * pm._44) + (pm._12 * pm._34 * pm._41) + (pm._14 143 | * pm._31 * pm._42)) 144 | - ((pm._14 * pm._32 * pm._41) + (pm._11 * pm._34 * pm._42) + (pm._12 145 | * pm._31 * pm._44)); 146 | 147 | a42 = ((pm._11 * pm._32 * pm._43) + (pm._12 * pm._33 * pm._41) + (pm._13 148 | * pm._31 * pm._42)) 149 | - ((pm._13 * pm._32 * pm._41) + (pm._11 * pm._33 * pm._42) + (pm._12 150 | * pm._31 * pm._43)); 151 | // 152 | // 153 | a13 = ((pm._12 * pm._23 * pm._44) + (pm._13 * pm._24 * pm._42) + (pm._14 154 | * pm._22 * pm._43)) 155 | - ((pm._14 * pm._23 * pm._42) + (pm._12 * pm._24 * pm._43) + (pm._13 156 | * pm._22 * pm._44)); 157 | 158 | a23 = ((pm._11 * pm._23 * pm._44) + (pm._13 * pm._24 * pm._41) + (pm._14 159 | * pm._21 * pm._43)) 160 | - ((pm._14 * pm._23 * pm._41) + (pm._11 * pm._24 * pm._43) + (pm._13 161 | * pm._21 * pm._44)); 162 | 163 | a33 = ((pm._11 * pm._22 * pm._44) + (pm._12 * pm._24 * pm._41) + (pm._14 164 | * pm._21 * pm._42)) 165 | - ((pm._14 * pm._22 * pm._41) + (pm._11 * pm._24 * pm._42) + (pm._12 166 | * pm._21 * pm._44)); 167 | 168 | a43 = ((pm._11 * pm._22 * pm._43) + (pm._12 * pm._23 * pm._41) + (pm._13 169 | * pm._21 * pm._42)) 170 | - ((pm._13 * pm._22 * pm._41) + (pm._11 * pm._23 * pm._42) + (pm._12 171 | * pm._21 * pm._43)); 172 | // 173 | // 174 | a14 = ((pm._12 * pm._23 * pm._34) + (pm._13 * pm._24 * pm._32) + (pm._14 175 | * pm._22 * pm._33)) 176 | - ((pm._14 * pm._23 * pm._32) + (pm._12 * pm._24 * pm._33) + (pm._13 177 | * pm._22 * pm._34)); 178 | 179 | a24 = ((pm._11 * pm._23 * pm._34) + (pm._13 * pm._24 * pm._31) + (pm._14 180 | * pm._21 * pm._33)) 181 | - ((pm._14 * pm._23 * pm._31) + (pm._11 * pm._24 * pm._33) + (pm._13 182 | * pm._21 * pm._34)); 183 | 184 | a34 = ((pm._11 * pm._22 * pm._34) + (pm._12 * pm._24 * pm._31) + (pm._14 185 | * pm._21 * pm._32)) 186 | - ((pm._14 * pm._22 * pm._31) + (pm._11 * pm._24 * pm._32) + (pm._12 187 | * pm._21 * pm._34)); 188 | 189 | a44 = ((pm._11 * pm._22 * pm._33) + (pm._12 * pm._23 * pm._31) + (pm._13 190 | * pm._21 * pm._32)) 191 | - ((pm._13 * pm._22 * pm._31) + (pm._11 * pm._23 * pm._32) + (pm._12 192 | * pm._21 * pm._33)); 193 | // 194 | 195 | pdest._11 = (+a11) * idet; 196 | pdest._12 = (-a12) * idet; 197 | pdest._13 = (+a13) * idet; 198 | pdest._14 = (-a14) * idet; 199 | // 200 | pdest._21 = (-a21) * idet; 201 | pdest._22 = (+a22) * idet; 202 | pdest._23 = (-a23) * idet; 203 | pdest._24 = (+a24) * idet; 204 | // 205 | pdest._31 = (+a31) * idet; 206 | pdest._32 = (-a32) * idet; 207 | pdest._33 = (+a33) * idet; 208 | pdest._34 = (-a34) * idet; 209 | // 210 | pdest._41 = (-a41) * idet; 211 | pdest._42 = (+a42) * idet; 212 | pdest._43 = (-a43) * idet; 213 | pdest._44 = (+a44) * idet; 214 | 215 | return true; 216 | } 217 | 218 | /** 219 | * Makes a given matrix to a identity matrix with a diagonal of 1.0f. 220 | * 221 | * @param pm The matrix to set. 222 | */ 223 | public static void identity(Matrix4f pm) { 224 | pm._11 = 1.0f; 225 | pm._12 = 0.0f; 226 | pm._13 = 0.0f; 227 | pm._14 = 0.0f; 228 | pm._21 = 0.0f; 229 | pm._22 = 1.0f; 230 | pm._23 = 0.0f; 231 | pm._24 = 0.0f; 232 | pm._31 = 0.0f; 233 | pm._32 = 0.0f; 234 | pm._33 = 1.0f; 235 | pm._34 = 0.0f; 236 | pm._41 = 0.0f; 237 | pm._42 = 0.0f; 238 | pm._43 = 0.0f; 239 | pm._44 = 1.0f; 240 | } 241 | 242 | /** 243 | * Sets a matrix to a 0 matrix. 244 | */ 245 | public static void zero(Matrix4f pm) { 246 | pm._11 = 0.0f; 247 | pm._12 = 0.0f; 248 | pm._13 = 0.0f; 249 | pm._14 = 0.0f; 250 | pm._21 = 0.0f; 251 | pm._22 = 0.0f; 252 | pm._23 = 0.0f; 253 | pm._24 = 0.0f; 254 | pm._31 = 0.0f; 255 | pm._32 = 0.0f; 256 | pm._33 = 0.0f; 257 | pm._34 = 0.0f; 258 | pm._41 = 0.0f; 259 | pm._42 = 0.0f; 260 | pm._43 = 0.0f; 261 | pm._44 = 0.0f; 262 | } 263 | 264 | // ---------------------------------------------------------------- 265 | // static methods 266 | // ---------------------------------------------------------------- 267 | 268 | /** 269 | * Returns a new a identity matrix with a diagonal of 1.0f. 270 | */ 271 | public static Matrix4f identity() { 272 | Matrix4f m = new Matrix4f(); 273 | identity(m); 274 | return m; 275 | } 276 | 277 | /** 278 | * Multiplies a matrix with another an stores the result in pmret. 279 | * 280 | * @param pm1 The left operand matrix (A). 281 | * @param pm2 The right operand matrix (B). 282 | * @param pmret The result matrix. 283 | */ 284 | public static void mul(Matrix4f pm1, Matrix4f pm2, Matrix4f pmret) { 285 | float c11, c12, c13, c14; 286 | float c21, c22, c23, c24; 287 | float c31, c32, c33, c34; 288 | float c41, c42, c43, c44; 289 | 290 | c11 = (pm1._11 * pm2._11) + (pm1._12 * pm2._21) + (pm1._13 * pm2._31) 291 | + (pm1._14 * pm2._41); 292 | c12 = (pm1._11 * pm2._12) + (pm1._12 * pm2._22) + (pm1._13 * pm2._32) 293 | + (pm1._14 * pm2._42); 294 | c13 = (pm1._11 * pm2._13) + (pm1._12 * pm2._23) + (pm1._13 * pm2._33) 295 | + (pm1._14 * pm2._43); 296 | c14 = (pm1._11 * pm2._14) + (pm1._12 * pm2._24) + (pm1._13 * pm2._34) 297 | + (pm1._14 * pm2._44); 298 | 299 | c21 = (pm1._21 * pm2._11) + (pm1._22 * pm2._21) + (pm1._23 * pm2._31) 300 | + (pm1._24 * pm2._41); 301 | c22 = (pm1._21 * pm2._12) + (pm1._22 * pm2._22) + (pm1._23 * pm2._32) 302 | + (pm1._24 * pm2._42); 303 | c23 = (pm1._21 * pm2._13) + (pm1._22 * pm2._23) + (pm1._23 * pm2._33) 304 | + (pm1._24 * pm2._43); 305 | c24 = (pm1._21 * pm2._14) + (pm1._22 * pm2._24) + (pm1._23 * pm2._34) 306 | + (pm1._24 * pm2._44); 307 | 308 | c31 = (pm1._31 * pm2._11) + (pm1._32 * pm2._21) + (pm1._33 * pm2._31) 309 | + (pm1._34 * pm2._41); 310 | c32 = (pm1._31 * pm2._12) + (pm1._32 * pm2._22) + (pm1._33 * pm2._32) 311 | + (pm1._34 * pm2._42); 312 | c33 = (pm1._31 * pm2._13) + (pm1._32 * pm2._23) + (pm1._33 * pm2._33) 313 | + (pm1._34 * pm2._43); 314 | c34 = (pm1._31 * pm2._14) + (pm1._32 * pm2._24) + (pm1._33 * pm2._34) 315 | + (pm1._34 * pm2._44); 316 | 317 | c41 = (pm1._41 * pm2._11) + (pm1._42 * pm2._21) + (pm1._43 * pm2._31) 318 | + (pm1._44 * pm2._41); 319 | c42 = (pm1._41 * pm2._12) + (pm1._42 * pm2._22) + (pm1._43 * pm2._32) 320 | + (pm1._44 * pm2._42); 321 | c43 = (pm1._41 * pm2._13) + (pm1._42 * pm2._23) + (pm1._43 * pm2._33) 322 | + (pm1._44 * pm2._43); 323 | c44 = (pm1._41 * pm2._14) + (pm1._42 * pm2._24) + (pm1._43 * pm2._34) 324 | + (pm1._44 * pm2._44); 325 | 326 | pmret._11 = c11; 327 | pmret._12 = c12; 328 | pmret._13 = c13; 329 | pmret._14 = c14; 330 | pmret._21 = c21; 331 | pmret._22 = c22; 332 | pmret._23 = c23; 333 | pmret._24 = c24; 334 | pmret._31 = c31; 335 | pmret._32 = c32; 336 | pmret._33 = c33; 337 | pmret._34 = c34; 338 | pmret._41 = c41; 339 | pmret._42 = c42; 340 | pmret._43 = c43; 341 | pmret._44 = c44; 342 | } 343 | 344 | /** 345 | * Return a new matrix within the result of the first matrix multiplied with 346 | * the second matrix. 347 | * 348 | * @param pm1 The left operand matrix. 349 | * @param pm2 The right operand matrix. 350 | * @return A new matrix. 351 | */ 352 | public static Matrix4f mul(Matrix4f pm1, Matrix4f pm2) { 353 | Matrix4f m = new Matrix4f(); 354 | mul(pm1, pm2, m); 355 | return m; 356 | } 357 | 358 | /** 359 | * Multiplies a matrix with a vector and stores the result which also is a 360 | * vector in pvret. 361 | * 362 | * @param pm The left operand matrix. 363 | * @param pv The right operand vector. 364 | * @param pvret The result vector. 365 | */ 366 | public static void mul(Matrix4f pm, Vector3f pv, Vector3f pvret) { 367 | float x; 368 | float y; 369 | float z; 370 | float w; 371 | float iw; 372 | 373 | x = (pm._11 * pv.x) + (pm._12 * pv.y) + (pm._13 * pv.z) + (pm._14); 374 | y = (pm._21 * pv.x) + (pm._22 * pv.y) + (pm._23 * pv.z) + (pm._24); 375 | z = (pm._31 * pv.x) + (pm._32 * pv.y) + (pm._33 * pv.z) + (pm._34); 376 | w = (pm._41 * pv.x) + (pm._42 * pv.y) + (pm._43 * pv.z) + (pm._44); 377 | 378 | iw = 1 / w; 379 | pvret.x = x * iw; 380 | pvret.y = y * iw; 381 | pvret.z = z * iw; 382 | } 383 | 384 | /** 385 | * Multiplies a matrix with a vector and returns the result as new vector. 386 | * 387 | * @param pm The left operand matrix. 388 | * @param pv The right operand vector. 389 | * @return A new vector. 390 | */ 391 | public static Vector3f mul(Matrix4f pm, Vector3f pv) { 392 | Vector3f v = new Vector3f(); 393 | mul(pm, pv, v); 394 | return v; 395 | } 396 | 397 | /** 398 | * Stores a matrix for rotation around the x-axis in pmret. 399 | * 400 | * @param pmret The matrix which is to fill. 401 | * @param pangle The angle of rotation. 402 | */ 403 | public static void rotateXMatrix(Matrix4f pmret, float pangle) { 404 | float cosa = (float) Math.cos(pangle); 405 | float sina = (float) Math.sin(pangle); 406 | 407 | identity(pmret); 408 | 409 | pmret._22 = cosa; 410 | pmret._23 = -sina; 411 | pmret._32 = sina; 412 | pmret._33 = cosa; 413 | } 414 | 415 | /** 416 | * Returns a new matrix for rotation around the x-axis. 417 | * 418 | * @param pangle The angle of rotation. 419 | */ 420 | public static Matrix4f rotateXMatrix(float pangle) { 421 | Matrix4f m = new Matrix4f(); 422 | rotateXMatrix(m, pangle); 423 | return m; 424 | } 425 | 426 | /** 427 | * Stores a matrix for rotation around the y-axis in pmret. 428 | * 429 | * @param pmret The matrix which is to fill. 430 | * @param pangle The angle of rotation. 431 | */ 432 | public static void rotateYMatrix(Matrix4f pmret, float pangle) { 433 | float cosa = (float) Math.cos(pangle); 434 | float sina = (float) Math.sin(pangle); 435 | 436 | identity(pmret); 437 | 438 | pmret._11 = cosa; 439 | pmret._13 = sina; 440 | pmret._31 = -sina; 441 | pmret._33 = cosa; 442 | } 443 | 444 | /** 445 | * Returns a new matrix for rotation around the y-axis. 446 | * 447 | * @param pangle The angle of rotation. 448 | */ 449 | public static Matrix4f rotateYMatrix(float pangle) { 450 | Matrix4f m = new Matrix4f(); 451 | rotateYMatrix(m, pangle); 452 | return m; 453 | } 454 | 455 | /** 456 | * Stores a matrix for rotation around the z-axis in pmret. 457 | * 458 | * @param pmret The matrix which is to fill. 459 | * @param pangle The angle of rotation. 460 | */ 461 | public static void rotateZMatrix(Matrix4f pmret, float pangle) { 462 | float cosa = (float) Math.cos(pangle); 463 | float sina = (float) Math.sin(pangle); 464 | 465 | identity(pmret); 466 | 467 | pmret._11 = cosa; 468 | pmret._12 = -sina; 469 | pmret._21 = sina; 470 | pmret._22 = cosa; 471 | } 472 | 473 | /** 474 | * Returns a new matrix for rotation around the z-axis. 475 | * 476 | * @param pangle The angle of rotation. 477 | */ 478 | public static Matrix4f rotateZMatrix(float pangle) { 479 | Matrix4f m = new Matrix4f(); 480 | rotateZMatrix(m, pangle); 481 | return m; 482 | } 483 | 484 | /** 485 | * Stores a matrix for scale x, y, z in pmret. 486 | * 487 | * @param pmret The matrix which is to fill. 488 | * @param px The x scale factor. 489 | * @param py The y scale factor. 490 | * @param pz The z scale factor. 491 | */ 492 | public static void scaleMatrix(Matrix4f pmret, float px, float py, float pz) { 493 | identity(pmret); 494 | 495 | pmret._11 = px; 496 | pmret._22 = py; 497 | pmret._33 = pz; 498 | } 499 | 500 | /** 501 | * Returns a new matrix for scale x, y, z 502 | * 503 | * @param px The x scale factor. 504 | * @param py The y scale factor. 505 | * @param pz The z scale factor. 506 | */ 507 | public static Matrix4f scaleMatrix(float px, float py, float pz) { 508 | Matrix4f m = new Matrix4f(); 509 | scaleMatrix(m, px, py, pz); 510 | return m; 511 | } 512 | 513 | /** 514 | * Stores a matrix for translate x, y, z in pmret. 515 | * 516 | * @param pmret The matrix which is to fill. 517 | * @param px The x translate offset. 518 | * @param py The y translate offset. 519 | * @param pz The z translate offset. 520 | */ 521 | public static void translateMatrix(Matrix4f pmret, float px, float py, 522 | float pz) { 523 | identity(pmret); 524 | 525 | pmret._14 = px; 526 | pmret._24 = py; 527 | pmret._34 = pz; 528 | } 529 | 530 | /** 531 | * Returns a new matrix for translate x, y, z 532 | * 533 | * @param px The x translate offset. 534 | * @param py The y translate offset. 535 | * @param pz The z translate offset. 536 | */ 537 | public static Matrix4f translateMatrix(float px, float py, float pz) { 538 | Matrix4f m = new Matrix4f(); 539 | translateMatrix(m, px, py, pz); 540 | return m; 541 | } 542 | 543 | /** 544 | * Copies the values of a given Matrix4f. 545 | * 546 | * @param pm A matrix for copy. 547 | */ 548 | public void copy(Matrix4f pm) { 549 | _11 = pm._11; 550 | _12 = pm._12; 551 | _13 = pm._13; 552 | _14 = pm._14; 553 | _21 = pm._21; 554 | _22 = pm._22; 555 | _23 = pm._23; 556 | _24 = pm._24; 557 | _31 = pm._31; 558 | _32 = pm._32; 559 | _33 = pm._33; 560 | _34 = pm._34; 561 | _41 = pm._41; 562 | _42 = pm._42; 563 | _43 = pm._43; 564 | _44 = pm._44; 565 | } 566 | 567 | public void setIdentity() { 568 | _11 = _22 = _33 = _44 = 1.0f; 569 | } 570 | 571 | public void map(float[] pdata) { 572 | pdata[0] = _11; 573 | pdata[1] = _12; 574 | pdata[2] = _13; 575 | pdata[3] = _14; 576 | // 577 | pdata[4] = _21; 578 | pdata[5] = _22; 579 | pdata[6] = _23; 580 | pdata[7] = _24; 581 | // 582 | pdata[8] = _31; 583 | pdata[9] = _32; 584 | pdata[10] = _33; 585 | pdata[11] = _34; 586 | // 587 | pdata[12] = _41; 588 | pdata[13] = _42; 589 | pdata[14] = _43; 590 | pdata[15] = _44; 591 | } 592 | 593 | /** 594 | * Converts matrix to string. 595 | */ 596 | @Override 597 | public String toString() { 598 | String res = ""; 599 | String tab = Character.toString((char) 9); 600 | 601 | res += this._11 + tab + this._12 + tab + this._13 + tab + this._14 602 | + "\n"; 603 | res += this._21 + tab + this._22 + tab + this._23 + tab + this._24 604 | + "\n"; 605 | res += this._31 + tab + this._32 + tab + this._33 + tab + this._34 606 | + "\n"; 607 | res += this._41 + tab + this._42 + tab + this._43 + tab + this._44 608 | + "\n"; 609 | 610 | return res; 611 | } 612 | 613 | public void setRotation(Quat4f q1) { 614 | float n, s; 615 | float xs, ys, zs; 616 | float wx, wy, wz; 617 | float xx, xy, xz; 618 | float yy, yz, zz; 619 | 620 | n = (q1.x * q1.x) + (q1.y * q1.y) + (q1.z * q1.z) + (q1.w * q1.w); 621 | s = (n > 0.0f) ? (2.0f / n) : 0.0f; 622 | 623 | xs = q1.x * s; 624 | ys = q1.y * s; 625 | zs = q1.z * s; 626 | wx = q1.w * xs; 627 | wy = q1.w * ys; 628 | wz = q1.w * zs; 629 | xx = q1.x * xs; 630 | xy = q1.x * ys; 631 | xz = q1.x * zs; 632 | yy = q1.y * ys; 633 | yz = q1.y * zs; 634 | zz = q1.z * zs; 635 | 636 | _11 = 1.0f - (yy + zz); 637 | _12 = xy - wz; 638 | _13 = xz + wy; 639 | _14 = 0f; 640 | _21 = xy + wz; 641 | _22 = 1.0f - (xx + zz); 642 | _23 = yz - wx; 643 | _24 = 0f; 644 | _31 = xz - wy; 645 | _32 = yz + wx; 646 | _33 = 1.0f - (xx + yy); 647 | _34 = 0f; 648 | _41 = 0f; 649 | _42 = 0f; 650 | _43 = 0f; 651 | _44 = 1f; 652 | 653 | } 654 | 655 | } 656 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/math/Quat4f.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.math; 7 | 8 | public class Quat4f { 9 | public float x, y, z, w; 10 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/math/Vector3f.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.math; 7 | 8 | public class Vector3f { 9 | public float x = 0.0f; 10 | public float y = 0.0f; 11 | public float z = 0.0f; 12 | 13 | /** 14 | * Constructs Vector3f by three float parameter. 15 | * 16 | * @param px The x value. 17 | * @param py The y value. 18 | * @param pz The z value. 19 | */ 20 | public Vector3f(float px, float py, float pz) { 21 | this.x = px; 22 | this.y = py; 23 | this.z = pz; 24 | } 25 | 26 | /** 27 | * Constructs Vector3f by an other Vector3f. 28 | * 29 | * @param pv Reference of other Vector3f. 30 | */ 31 | public Vector3f(Vector3f pv) { 32 | this.x = pv.x; 33 | this.y = pv.y; 34 | this.z = pv.z; 35 | } 36 | 37 | /** 38 | * Constructs an 0 initialized Vector3f. 39 | */ 40 | public Vector3f() { 41 | // 42 | } 43 | 44 | /** 45 | * Adds two vectors and stores the result into third vector. 46 | * 47 | * @param pv1 The left operand vector. 48 | * @param pv2 The right operand vector. 49 | * @param pvret The result vector. 50 | */ 51 | public static void add(Vector3f pv1, Vector3f pv2, Vector3f pvret) { 52 | pvret.x = pv1.x + pv2.x; 53 | pvret.y = pv1.y + pv2.y; 54 | pvret.z = pv1.z + pv2.z; 55 | } 56 | 57 | /** 58 | * Adds two vectors and returns a new vector within the result. 59 | * 60 | * @param pv1 The left operand vector. 61 | * @param pv2 The right operand vector. 62 | * @return A new vector within the result of the operation. 63 | */ 64 | public static Vector3f add(Vector3f pv1, Vector3f pv2) { 65 | return new Vector3f(pv1.x + pv2.x, pv1.y + pv2.y, pv1.z + pv2.z); 66 | } 67 | 68 | /** 69 | * Subtracts two vectors and stores the result into third vector. 70 | * 71 | * @param pv1 The left operand vector. 72 | * @param pv2 The right operand vector. 73 | * @param pvret The result vector. 74 | */ 75 | public static void sub(Vector3f pv1, Vector3f pv2, Vector3f pvret) { 76 | pvret.x = pv1.x - pv2.x; 77 | pvret.y = pv1.y - pv2.y; 78 | pvret.z = pv1.z - pv2.z; 79 | } 80 | 81 | /** 82 | * Subtracts two vectors and returns a new vector within the result. 83 | * 84 | * @param pv1 The left operand vector. 85 | * @param pv2 The right operand vector. 86 | * @return A new vector within the result of the operation. 87 | */ 88 | public static Vector3f sub(Vector3f pv1, Vector3f pv2) { 89 | return new Vector3f(pv1.x - pv2.x, pv1.y - pv2.y, pv1.z - pv2.z); 90 | } 91 | 92 | /** 93 | * Multiplies a vector with a scalar and stores the result in second vector. 94 | * 95 | * @param pv The left operand vector. 96 | * @param psc The right operand scalar. 97 | * @param pvret The result vector. 98 | */ 99 | public static void mul(Vector3f pv, float psc, Vector3f pvret) { 100 | pvret.x = pv.x * psc; 101 | pvret.y = pv.y * psc; 102 | pvret.z = pv.z * psc; 103 | } 104 | 105 | // ---------------------------------------------------------------- 106 | // static methods 107 | // ---------------------------------------------------------------- 108 | 109 | /** 110 | * Multiplies a vector with a scalar and returns the result as new vector. 111 | * 112 | * @param pv The left operand vector. 113 | * @param psc The right operand scalar. 114 | * @return A new vector within the result of the operation. 115 | */ 116 | public static Vector3f mul(Vector3f pv, float psc) { 117 | return new Vector3f(pv.x * psc, pv.y * psc, pv.z * psc); 118 | } 119 | 120 | /** 121 | * Divides a vector by a scalar and stores the result in second vector. 122 | * 123 | * @param pv The left operand vector. 124 | * @param psc The right operand scalar. 125 | * @param pvret The result vector. 126 | */ 127 | public static void div(Vector3f pv, float psc, Vector3f pvret) { 128 | float iv = 1 / psc; 129 | pvret.x = pv.x * iv; 130 | pvret.y = pv.y * iv; 131 | pvret.z = pv.z * iv; 132 | } 133 | 134 | /** 135 | * Divides a vector by a scalar and returns the result as new vector. 136 | * 137 | * @param pv The left operand vector. 138 | * @param psc The right operand scalar. 139 | * @return A new vector within the result of the operation. 140 | */ 141 | public static Vector3f div(Vector3f pv, float psc) { 142 | float iv = 1 / psc; 143 | return new Vector3f(pv.x * iv, pv.y * iv, pv.z * iv); 144 | } 145 | 146 | /** 147 | * Normalizes a vector and stores the result that is a vector in the same 148 | * direction with length of 1 in pvret. 149 | * 150 | * @param pv Source vector. 151 | * @param pvret Normalized vector. 152 | */ 153 | public static void normalize(Vector3f pv, Vector3f pvret) { 154 | float l = 1 / pv.length(); 155 | pvret.x = pv.x * l; 156 | pvret.y = pv.y * l; 157 | pvret.z = pv.z * l; 158 | } 159 | 160 | /** 161 | * Normalizes a vector and returns the result that is a vector in the same 162 | * direction with length of 1 as a new vector. 163 | * 164 | * @param pv The source vector. 165 | * @return A new normalized vector. 166 | */ 167 | public static Vector3f normalize(Vector3f pv) { 168 | float l = 1 / pv.length(); 169 | return new Vector3f(pv.x * l, pv.y * l, pv.z * l); 170 | } 171 | 172 | /** 173 | * Inverts a given vector and stores the result into pvret. 174 | * 175 | * @param pv The source vector. 176 | * @param pvret Inverted vector. 177 | */ 178 | public static void invert(Vector3f pv, Vector3f pvret) { 179 | pvret.x = -pv.x; 180 | pvret.y = -pv.y; 181 | pvret.z = -pv.z; 182 | } 183 | 184 | /** 185 | * Inverts a given vector and returns a new inverted vector. 186 | * 187 | * @param pv The source vector. 188 | * @return A new inverted vector. 189 | */ 190 | public static Vector3f invert(Vector3f pv) { 191 | return new Vector3f(-pv.x, -pv.y, -pv.z); 192 | } 193 | 194 | /** 195 | * Returns the scalar product of two vectors. 196 | * 197 | * @param pv1 Left operand vector. 198 | * @param pv2 Right operand vector. 199 | * @return The scalar product. 200 | */ 201 | public static float scalar(Vector3f pv1, Vector3f pv2) { 202 | return (pv1.x * pv2.x) + (pv1.y * pv2.y) + (pv1.z * pv2.z); 203 | } 204 | 205 | /** 206 | * Builds the cross product of two vectors an stores the result into pvret. 207 | * 208 | * @param pv1 Left operand vector. 209 | * @param pv2 Right operand vector. 210 | * @param pvret The result vector. 211 | */ 212 | public static void cross(Vector3f pv1, Vector3f pv2, Vector3f pvret) { 213 | pvret.x = (pv1.y * pv2.z) - (pv1.z * pv2.y); 214 | pvret.y = (pv1.z * pv2.x) - (pv1.x * pv2.z); 215 | pvret.z = (pv1.x * pv2.y) - (pv1.y * pv2.x); 216 | } 217 | 218 | public static float dot(Vector3f v1, Vector3f v2) { 219 | return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z + v2.z); 220 | } 221 | 222 | /** 223 | * Returns the cross product of two vectors. 224 | * 225 | * @param pv1 Left operand vector. 226 | * @param pv2 Right operand vector. 227 | * @return A new vector within the result of the operation. 228 | */ 229 | public static Vector3f cross(Vector3f pv1, Vector3f pv2) { 230 | return new Vector3f((pv1.y * pv2.z) - (pv1.z * pv2.y), 231 | (pv1.z * pv2.x) - (pv1.x * pv2.z), (pv1.x * pv2.y) - (pv1.y * pv2.x)); 232 | } 233 | 234 | /** 235 | * Returns the angle (phi) between two vectors. 236 | * 237 | * @param pv1 The first vector. 238 | * @param pv2 The second vector. 239 | * @return The angle. 240 | */ 241 | public static float phi(Vector3f pv1, Vector3f pv2) { 242 | return (float) Math.acos(scalar(pv1, pv2) / (pv1.length() * pv2.length())); 243 | } 244 | 245 | /** 246 | * Rotates a vector around the x-axis and stores the result into pvret. 247 | * 248 | * @param pv The vector which is to rotate. 249 | * @param pangle The angle in rad of the rotation. 250 | * @param pvret The return vector. 251 | */ 252 | public static void rotateX(Vector3f pv, float pangle, Vector3f pvret) { 253 | float cosa = (float) Math.cos(pangle); 254 | float sina = (float) Math.sin(pangle); 255 | 256 | pvret.x = pv.x; 257 | pvret.y = (pv.y * cosa) + (pv.z * -sina); 258 | pvret.z = (pv.y * sina) + (pv.z * cosa); 259 | } 260 | 261 | /** 262 | * Rotates a vector around the x-axis and returns the result as new vector. 263 | * 264 | * @param pv The vector which is to rotate. 265 | * @param pangle The angle in rad of the rotation. 266 | * @return The return vector. 267 | */ 268 | public static Vector3f rotateX(Vector3f pv, float pangle) { 269 | Vector3f v = new Vector3f(); 270 | rotateX(pv, pangle, v); 271 | return v; 272 | } 273 | 274 | /** 275 | * Rotates a vector around the y-axis and stores the result into pvret. 276 | * 277 | * @param pv The vector which is to rotate. 278 | * @param pangle The angle in rad of the rotation. 279 | * @param pvret The return vector. 280 | */ 281 | public static void rotateY(Vector3f pv, float pangle, Vector3f pvret) { 282 | float cosa = (float) Math.cos(pangle); 283 | float sina = (float) Math.sin(pangle); 284 | 285 | pvret.x = (pv.x * cosa) + (pv.z * sina); 286 | pvret.y = pv.y; 287 | pvret.z = (pv.x * -sina) + (pv.z * cosa); 288 | } 289 | 290 | /** 291 | * Rotates a vector around the y-axis and returns the result as new vector. 292 | * 293 | * @param pv The vector which is to rotate. 294 | * @param pangle The angle in rad of the rotation. 295 | * @return The return vector. 296 | */ 297 | public static Vector3f rotateY(Vector3f pv, float pangle) { 298 | Vector3f v = new Vector3f(); 299 | rotateY(pv, pangle, v); 300 | return v; 301 | } 302 | 303 | /** 304 | * Rotates a vector around the z-axis and stores the result into pvret. 305 | * 306 | * @param pv The vector which is to rotate. 307 | * @param pangle The angle in rad of the rotation. 308 | * @param pvret The return vector. 309 | */ 310 | public static void rotateZ(Vector3f pv, float pangle, Vector3f pvret) { 311 | // 312 | float cosa = (float) Math.cos(pangle); 313 | float sina = (float) Math.sin(pangle); 314 | 315 | pvret.x = (pv.x * cosa) + (pv.y * -sina); 316 | pvret.y = (pv.x * sina) + (pv.y * cosa); 317 | pvret.z = pv.z; 318 | } 319 | 320 | /** 321 | * Rotates a vector around the z-axis and returns the result as new vector. 322 | * 323 | * @param pv The vector which is to rotate. 324 | * @param pangle The angle in rad of the rotation. 325 | * @return The return vector. 326 | */ 327 | public static Vector3f rotateZ(Vector3f pv, float pangle) { 328 | // 329 | Vector3f v = new Vector3f(); 330 | rotateZ(pv, pangle, v); 331 | return v; 332 | } 333 | 334 | /** 335 | * The methods builds the normalized plane normal of the input vectors and 336 | * stores the result into pvret. Note that this method uses the right hand 337 | * rule.
338 | *
339 | * 340 | * @param pvec1 The first vector "a". 341 | * @param pvec2 The second vector "b". 342 | * @param pvec3 The third vector "c". 343 | * @param pvret The returning vector. 344 | */ 345 | public static void normal( 346 | Vector3f pvec1, Vector3f pvec2, Vector3f pvec3, Vector3f pvret) { 347 | // 348 | // Build plane with two vectors with source point pvec1. 349 | // 350 | Vector3f ab = Vector3f.sub(pvec2, pvec1); 351 | Vector3f ac = Vector3f.sub(pvec3, pvec1); 352 | // 353 | // Build the cross product to get the plane normal, and normalize the 354 | // vector. 355 | // 356 | Vector3f.cross(ab, ac, pvret); 357 | Vector3f.normalize(pvret, pvret); 358 | } 359 | 360 | /** 361 | * The methods builds the normalized plane normal of the input vectors and 362 | * returns the result as a new vector. Note that this method uses the right 363 | * hand rule.
364 | *
365 | * 366 | * @param pvec1 The first vector "a". 367 | * @param pvec2 The second vector "b". 368 | * @param pvec3 The third vector "c". 369 | */ 370 | public static Vector3f normal(Vector3f pvec1, Vector3f pvec2, Vector3f pvec3) { 371 | // 372 | Vector3f ret = new Vector3f(); 373 | normal(pvec1, pvec2, pvec3, ret); 374 | return ret; 375 | } 376 | 377 | @Override 378 | public boolean equals(Object pvec) { 379 | if (!(pvec instanceof Vector3f)) 380 | return false; 381 | 382 | Vector3f vec = (Vector3f) pvec; 383 | return ((this.x == vec.x) && (this.y == vec.y) && (this.z == vec.z)); 384 | } 385 | 386 | /** 387 | * Copies the values of a given Vector3f. 388 | * 389 | * @param pv A vector for copy. 390 | */ 391 | public void copy(Vector3f pv) { 392 | this.x = pv.x; 393 | this.y = pv.y; 394 | this.z = pv.z; 395 | } 396 | 397 | /** 398 | * Return the length of the vector in R3. 399 | * 400 | * @return Vector length. 401 | */ 402 | public float length() { 403 | return (float) Math.sqrt( 404 | (this.x * this.x) + (this.y * this.y) + (this.z * this.z)); 405 | } 406 | 407 | /** 408 | * Return the length of the vector in R3 without sqrt. 409 | * 410 | * @return Vector length. 411 | */ 412 | public float length2() { 413 | return (this.x * this.x) + (this.y * this.y) + (this.z * this.z); 414 | } 415 | 416 | public String toString() { 417 | return "Vector3f(" + this.x + "," + this.y + "," + this.z + ")"; 418 | } 419 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/rendering/ObjectModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.rendering; 7 | 8 | import android.graphics.Bitmap; 9 | 10 | import java.io.IOException; 11 | import java.io.ObjectInputStream; 12 | import java.io.ObjectOutputStream; 13 | import java.io.Serializable; 14 | import java.nio.ByteBuffer; 15 | import java.nio.ByteOrder; 16 | import java.nio.FloatBuffer; 17 | import java.nio.ShortBuffer; 18 | 19 | import javax.microedition.khronos.opengles.GL10; 20 | 21 | import de.hsrm.objectify.utils.ArrayUtils; 22 | 23 | /** 24 | * A representation of a 3D model object. Vertices, mNormals and mTexture can be added 25 | * after an instance of this class is created. This class implements {@link Serializable} 26 | * for saving/loading an instance of it 27 | */ 28 | public class ObjectModel implements Serializable { 29 | private static final String TAG = "ObjectModel"; 30 | private static final long serialVersionUID = 0L; 31 | public byte[] mBitmapData; 32 | private transient FloatBuffer mVertexBuffer; 33 | private transient FloatBuffer mTextureBuffer; 34 | private transient FloatBuffer normalsBuffer; 35 | private transient ShortBuffer mFacesBuffer; 36 | private int[] mTextures = new int[1]; 37 | private float[] mTexture; 38 | private float mVertices[]; 39 | private float mNormals[]; 40 | private short mFaces[]; 41 | private float[] mBoundingBox; 42 | 43 | public ObjectModel(float[] vertices, float[] normals, short[] faces) { 44 | onInitialize(vertices, normals, faces); 45 | } 46 | 47 | private void onInitialize(float[] vertices, float[] normals, short[] faces) { 48 | setVertices(vertices); 49 | setNormalVertices(normals); 50 | setFaces(faces); 51 | ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4); 52 | byteBuf.order(ByteOrder.nativeOrder()); 53 | mVertexBuffer = byteBuf.asFloatBuffer(); 54 | mVertexBuffer.put(vertices); 55 | mVertexBuffer.rewind(); 56 | 57 | byteBuf = ByteBuffer.allocateDirect(normals.length * 4); 58 | byteBuf.order(ByteOrder.nativeOrder()); 59 | normalsBuffer = byteBuf.asFloatBuffer(); 60 | normalsBuffer.put(normals); 61 | normalsBuffer.rewind(); 62 | 63 | byteBuf = ByteBuffer.allocateDirect(faces.length * 4); 64 | byteBuf.order(ByteOrder.nativeOrder()); 65 | mFacesBuffer = byteBuf.asShortBuffer(); 66 | mFacesBuffer.put(faces); 67 | mFacesBuffer.rewind(); 68 | } 69 | 70 | public void setTextureBitmap(Bitmap texture) { 71 | Bitmap textureBitmap = texture.copy(texture.getConfig(), true); 72 | } 73 | 74 | public int getVerticesSize() { 75 | return mVertices.length; 76 | } 77 | 78 | public int getFacesSize() { 79 | return mFaces.length; 80 | } 81 | 82 | public void setNormalVertices(float[] normals) { 83 | this.mNormals = new float[normals.length]; 84 | System.arraycopy(normals, 0, this.mNormals, 0, normals.length); 85 | setNormalBuffer(this.mNormals); 86 | } 87 | 88 | public short[] getFaces() { 89 | return mFaces; 90 | } 91 | 92 | public void setFaces(short[] face) { 93 | this.mFaces = new short[face.length]; 94 | System.arraycopy(face, 0, mFaces, 0, face.length); 95 | setFacesBuffer(this.mFaces); 96 | } 97 | 98 | private void setVertexBuffer(float[] vertices) { 99 | ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); 100 | vbb.order(ByteOrder.nativeOrder()); 101 | this.mVertexBuffer = vbb.asFloatBuffer(); 102 | for (float f : vertices) { 103 | mVertexBuffer.put(f); 104 | } 105 | mVertexBuffer.rewind(); 106 | } 107 | 108 | private void setNormalBuffer(float[] normal) { 109 | ByteBuffer nbb = ByteBuffer.allocateDirect(normal.length * 4); 110 | nbb.order(ByteOrder.nativeOrder()); 111 | this.normalsBuffer = nbb.asFloatBuffer(); 112 | for (float f : mNormals) { 113 | normalsBuffer.put(f); 114 | } 115 | normalsBuffer.rewind(); 116 | } 117 | 118 | private void setFacesBuffer(short[] faces) { 119 | ByteBuffer fbb = ByteBuffer.allocateDirect(faces.length * 2); 120 | fbb.order(ByteOrder.nativeOrder()); 121 | mFacesBuffer = fbb.asShortBuffer(); 122 | for (short s : faces) { 123 | mFacesBuffer.put(s); 124 | } 125 | mFacesBuffer.rewind(); 126 | } 127 | 128 | public float getLength() { 129 | if (mBoundingBox == null) { 130 | setupBoundingBox(); 131 | } 132 | float[] tmp = new float[] {(mBoundingBox[1] - mBoundingBox[0]), 133 | (mBoundingBox[3] - mBoundingBox[2]), (mBoundingBox[5] - mBoundingBox[4])}; 134 | return 2.0f / ArrayUtils.max(tmp); 135 | } 136 | 137 | private void setupBoundingBox() { 138 | float x1 = 0, x2 = 0, y1 = 0, y2 = 0, z1 = 0, z2 = 0; 139 | x1 = ArrayUtils.min(mVertices, 0); 140 | x2 = ArrayUtils.max(mVertices, 0); 141 | y1 = ArrayUtils.min(mVertices, 1); 142 | y2 = ArrayUtils.max(mVertices, 1); 143 | z1 = ArrayUtils.min(mVertices, 2); 144 | z2 = ArrayUtils.max(mVertices, 2); 145 | mBoundingBox = new float[] {x1, x2, y1, y2, z1, z2}; 146 | } 147 | 148 | /** 149 | * Returns the middle point of this objects' mBoundingBox 150 | * 151 | * @return the middle point of this object. 152 | */ 153 | public float[] getMiddlePoint() { 154 | if (mBoundingBox == null) { 155 | setupBoundingBox(); 156 | } 157 | float xmiddle = (mBoundingBox[0] + mBoundingBox[1]) / 2.0f; 158 | float ymiddle = (mBoundingBox[2] + mBoundingBox[3]) / 2.0f; 159 | float zmiddle = (mBoundingBox[4] + mBoundingBox[5]) / 2.0f; 160 | return new float[] {xmiddle, ymiddle, zmiddle}; 161 | } 162 | 163 | public float[] getVertices() { 164 | return mVertices; 165 | } 166 | 167 | public void setVertices(float[] mVertices) { 168 | this.mVertices = new float[mVertices.length]; 169 | System.arraycopy(mVertices, 0, this.mVertices, 0, mVertices.length); 170 | setVertexBuffer(this.mVertices); 171 | } 172 | 173 | public void draw(GL10 gl) { 174 | gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 175 | gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); 176 | 177 | gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer); 178 | gl.glNormalPointer(GL10.GL_FLOAT, 0, normalsBuffer); 179 | 180 | int renderMode = GL10.GL_LINES; 181 | gl.glDrawElements( 182 | renderMode, mFaces.length, GL10.GL_UNSIGNED_SHORT, mFacesBuffer); 183 | 184 | gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); 185 | gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 186 | } 187 | 188 | private void writeObject(ObjectOutputStream out) throws IOException { 189 | /* write vertices */ 190 | out.writeInt(mVertices.length); 191 | for (int i = 0; i < mVertices.length; i++) { 192 | out.writeFloat(mVertices[i]); 193 | } 194 | 195 | /* write surface normals */ 196 | out.writeInt(mNormals.length); 197 | for (int i = 0; i < mNormals.length; i++) { 198 | out.writeFloat(mNormals[i]); 199 | } 200 | 201 | /* write faces */ 202 | out.writeInt(mFaces.length); 203 | for (int i = 0; i < mFaces.length; i++) { 204 | out.writeShort(mFaces[i]); 205 | } 206 | out.flush(); 207 | } 208 | 209 | private void readObject(ObjectInputStream in) 210 | throws IOException, ClassNotFoundException { 211 | /* read vertices */ 212 | float[] vertices = new float[in.readInt()]; 213 | for (int i = 0; i < vertices.length; i++) { 214 | vertices[i] = in.readFloat(); 215 | } 216 | 217 | /* read surface normals */ 218 | float[] normals = new float[in.readInt()]; 219 | for (int i = 0; i < normals.length; i++) { 220 | normals[i] = in.readFloat(); 221 | } 222 | 223 | /* read faces */ 224 | short[] faces = new short[in.readInt()]; 225 | for (int i = 0; i < faces.length; i++) { 226 | faces[i] = in.readShort(); 227 | } 228 | onInitialize(vertices, normals, faces); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/rendering/ReconstructionService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.rendering; 7 | 8 | import android.app.IntentService; 9 | import android.content.ContentResolver; 10 | import android.content.ContentValues; 11 | import android.content.Intent; 12 | import android.graphics.Bitmap; 13 | import android.graphics.Color; 14 | import android.net.Uri; 15 | import android.renderscript.Allocation; 16 | import android.renderscript.Element; 17 | import android.renderscript.RenderScript; 18 | import android.renderscript.Type; 19 | 20 | import org.ejml.data.DenseMatrix64F; 21 | import org.ejml.factory.DecompositionFactory; 22 | import org.ejml.interfaces.decomposition.SingularValueDecomposition; 23 | import org.ejml.ops.CommonOps; 24 | 25 | import java.io.FileOutputStream; 26 | import java.io.ObjectOutputStream; 27 | import java.nio.FloatBuffer; 28 | import java.nio.ShortBuffer; 29 | import java.util.ArrayList; 30 | import java.util.Calendar; 31 | 32 | import de.hsrm.objectify.camera.Constants; 33 | import de.hsrm.objectify.database.DatabaseAdapter; 34 | import de.hsrm.objectify.database.DatabaseProvider; 35 | import de.hsrm.objectify.rendering.compute_normals.ScriptC_compute_normals; 36 | import de.hsrm.objectify.rendering.lh_integration.ScriptC_lh_integration; 37 | import de.hsrm.objectify.utils.ArrayUtils; 38 | import de.hsrm.objectify.utils.BitmapUtils; 39 | import de.hsrm.objectify.utils.Size; 40 | import de.hsrm.objectify.utils.Storage; 41 | 42 | import static android.renderscript.Type.Builder; 43 | 44 | public class ReconstructionService extends IntentService { 45 | public static final String DIRECTORY_NAME = "dir_name"; 46 | public static final String NOTIFICATION = 47 | "de.hsrm.objectify.android.service.receiver"; 48 | public static final String GALLERY_ID = "gallery_id"; 49 | public static final String MODEL_NAME = "model.kaw"; 50 | public static final String NORMAL_IMG_NAME = "normals.png"; 51 | public static final String HEIGHT_IMG_NAME = "heights.png"; 52 | private static final int LH_ITERATIONS = 3000; 53 | private int mWidth; 54 | private int mHeight; 55 | 56 | public ReconstructionService() { 57 | super("ReconstructionService"); 58 | } 59 | 60 | private ArrayList readImages(String dirName) { 61 | ArrayList images = new ArrayList(); 62 | /* i from 0 to number of images + ambient image */ 63 | for (int i = 0; i <= Constants.NUM_IMAGES; i++) { 64 | Bitmap img = 65 | BitmapUtils.openBitmap(Storage.getExternalRootDirectory() + "/" + dirName 66 | + "/" + Constants.IMAGE_NAME + i + "." + Constants.IMAGE_FORMAT); 67 | images.add(img); 68 | } 69 | 70 | return images; 71 | } 72 | 73 | @Override 74 | protected void onHandleIntent(Intent intent) { 75 | /* get images */ 76 | String dirName = intent.getStringExtra(DIRECTORY_NAME); 77 | ArrayList images = readImages(dirName); 78 | 79 | mWidth = images.get(0).getWidth(); 80 | mHeight = images.get(0).getHeight(); 81 | /* subtract first ambient image from the remaining images */ 82 | Bitmap ambient = images.remove(0); 83 | for (int i = 0; i < images.size(); i++) { 84 | images.set(i, BitmapUtils.subtract(images.get(i), ambient)); 85 | } 86 | // images.clear(); 87 | // for (int i = 0; i < Constants.NUM_IMAGES; i++) { 88 | // images.add(BitmapUtils.openBitmap(Storage.getExternalRootDirectory() 89 | // + 90 | // "/" + dirName + "/kai_small_" + i + ".png")); 91 | // } 92 | 93 | /* compute normals */ 94 | float[] normals = computeNormals( 95 | images, BitmapUtils.convertToGrayscale(BitmapUtils.binarize(images.get(2)))); 96 | Bitmap Normals = BitmapUtils.convert(normals, mWidth, mHeight); 97 | BitmapUtils.saveBitmap(Normals, dirName, NORMAL_IMG_NAME); 98 | 99 | /* TODO: linear transformation depending on image size */ 100 | float[] Z = localHeightfield(normals); 101 | Z = ArrayUtils.linearTransform(Z, 0.0f, 50.0f); 102 | int[] heightPixels = new int[mWidth * mHeight]; 103 | int idx = 0; 104 | for (int i = 0; i < mHeight; i++) { 105 | for (int j = 0; j < mWidth; j++) { 106 | int z = (int) Z[i * mWidth + j]; 107 | heightPixels[idx++] = Color.rgb(z, z, z); 108 | } 109 | } 110 | 111 | Bitmap Height = 112 | Bitmap.createBitmap(heightPixels, mWidth, mHeight, Bitmap.Config.ARGB_8888); 113 | BitmapUtils.saveBitmap(Height, dirName, HEIGHT_IMG_NAME); 114 | 115 | ObjectModel obj = createObjectModel(Z, normals, images.get(2)); 116 | // OBJExport.write(obj, images.get(0), dirName); 117 | String galleryId = writeDatabaseEntry(obj, new Size(mWidth, mHeight), dirName); 118 | 119 | /* clean up and publish results */ 120 | publishResult(galleryId); 121 | } 122 | 123 | private ObjectModel createObjectModel( 124 | float[] heights, float[] normals, Bitmap texture) { 125 | FloatBuffer vertBuf = FloatBuffer.allocate(mWidth * mHeight * 3); 126 | FloatBuffer normBuf = FloatBuffer.allocate(mWidth * mHeight * 3); 127 | ArrayList indexes = new ArrayList(); 128 | vertBuf.rewind(); 129 | normBuf.rewind(); 130 | 131 | /* vertices and normals */ 132 | int idx = 0; 133 | for (int y = 0; y < mHeight; y++) { 134 | for (int x = 0; x < mWidth; x++) { 135 | float[] imgPt = 136 | new float[] {Float.valueOf(x), Float.valueOf(y), heights[idx]}; 137 | float[] nVec = new float[] { 138 | normals[4 * idx + 0], normals[4 * idx + 1], normals[4 * idx + 2]}; 139 | vertBuf.put(imgPt); 140 | normBuf.put(nVec); 141 | idx += 1; 142 | } 143 | } 144 | 145 | /* faces */ 146 | for (int i = 0; i < mHeight - 1; i++) { 147 | for (int j = 0; j < mWidth - 1; j++) { 148 | short index = (short) (j + (i * mWidth)); 149 | indexes.add(index); 150 | indexes.add((short) (index + mWidth)); 151 | indexes.add((short) (index + 1)); 152 | 153 | indexes.add((short) (index + 1)); 154 | indexes.add((short) (index + mWidth)); 155 | indexes.add((short) (index + mWidth + 1)); 156 | } 157 | } 158 | 159 | ShortBuffer indexBuf = ShortBuffer.allocate(indexes.size()); 160 | indexBuf.rewind(); 161 | for (int i = 0; i < indexes.size(); i++) { 162 | indexBuf.put(indexes.get(i)); 163 | } 164 | 165 | return new ObjectModel(vertBuf.array(), normBuf.array(), indexBuf.array()); 166 | } 167 | 168 | private String writeDatabaseEntry(ObjectModel objectModel, Size dim, String dirName) { 169 | /* initialize content resolver and database write */ 170 | ContentResolver cr = getContentResolver(); 171 | ContentValues values = new ContentValues(); 172 | Uri objUri = DatabaseProvider.CONTENT_URI.buildUpon() 173 | .appendPath(DatabaseAdapter.DATABASE_TABLE_OBJECT) 174 | .build(); 175 | Uri galleryUri = DatabaseProvider.CONTENT_URI.buildUpon() 176 | .appendPath(DatabaseAdapter.DATABASE_TABLE_GALLERY) 177 | .build(); 178 | 179 | /* get timestamp for saving into database */ 180 | Calendar cal = Calendar.getInstance(); 181 | String date = String.valueOf(cal.getTimeInMillis()); 182 | 183 | /* write 3d reconstruction to disk */ 184 | String filePath = 185 | Storage.getExternalRootDirectory() + "/" + dirName + "/" + MODEL_NAME; 186 | try { 187 | ObjectOutputStream objOutput = 188 | new ObjectOutputStream(new FileOutputStream(filePath)); 189 | objOutput.writeObject(objectModel); 190 | objOutput.close(); 191 | 192 | /* write object database entry */ 193 | values.put(DatabaseAdapter.OBJECT_FILE_PATH_KEY, filePath); 194 | Uri objResultUri = cr.insert(objUri, values); 195 | String objectID = objResultUri.getLastPathSegment(); 196 | values.clear(); 197 | 198 | /* write gallery database entry */ 199 | values.put(DatabaseAdapter.GALLERY_IMAGE_PATH_KEY, dirName); 200 | values.put(DatabaseAdapter.GALLERY_DATE_KEY, date); 201 | values.put(DatabaseAdapter.GALLERY_DIMENSION_KEY, dim.toString()); 202 | values.put(DatabaseAdapter.GALLERY_FACES_KEY, objectModel.getFacesSize()); 203 | values.put( 204 | DatabaseAdapter.GALLERY_VERTICES_KEY, objectModel.getVerticesSize()); 205 | values.put(DatabaseAdapter.GALLERY_OBJECT_ID_KEY, objectID); 206 | Uri galleryResultUri = cr.insert(galleryUri, values); 207 | return galleryResultUri.getLastPathSegment(); 208 | 209 | } catch (Exception e) { 210 | e.printStackTrace(); 211 | } 212 | 213 | return null; 214 | } 215 | 216 | private float[] localHeightfield(float[] normals) { 217 | /* create RenderScript context used to communicate with RenderScript. Afterwards 218 | * create the 219 | * actual script, which will do the real work */ 220 | RenderScript rs = RenderScript.create(getApplicationContext()); 221 | ScriptC_lh_integration lhIntegration = new ScriptC_lh_integration(rs); 222 | 223 | /* set params for the generator */ 224 | lhIntegration.set_width(mWidth); 225 | lhIntegration.set_height(mHeight); 226 | 227 | /* create allocation input to RenderScript. Avoid error since API 20: 228 | API 20+ only allows simple 1D allocations to be used with bind. */ 229 | Type normType = 230 | new Builder(rs, Element.F32_4(rs)).setX(mWidth * mHeight).create(); 231 | 232 | Allocation allInNormals = Allocation.createTyped(rs, normType); 233 | allInNormals.copyFromUnchecked(normals); 234 | 235 | Type heightType = 236 | new Builder(rs, Element.F32(rs)).setX(mWidth * mHeight).create(); 237 | Allocation allOutHeights = Allocation.createTyped(rs, heightType); 238 | 239 | /* bind normals and heights data to pNormals and pHeights pointer inside 240 | * RenderScript */ 241 | lhIntegration.bind_pNormals(allInNormals); 242 | lhIntegration.bind_pHeights(allOutHeights); 243 | 244 | /* pass the input to RenderScript */ 245 | for (int i = 0; i < LH_ITERATIONS; i++) { 246 | lhIntegration.forEach_integrate(allInNormals, allOutHeights); 247 | } 248 | 249 | /* save output from RenderScript */ 250 | float[] heights = new float[mWidth * mHeight]; 251 | allOutHeights.copyTo(heights); 252 | 253 | return heights; 254 | } 255 | 256 | private float[] computeNormals(ArrayList images, Bitmap Mask) { 257 | /* populate A */ 258 | double[][] a = new double[mWidth * mHeight][Constants.NUM_IMAGES]; 259 | int[] imgData = new int[mWidth * mHeight]; 260 | for (int k = 0; k < Constants.NUM_IMAGES; k++) { 261 | int idx = 0; 262 | images.get(k).getPixels(imgData, 0, mWidth, 0, 0, mWidth, mHeight); 263 | for (int i = 0; i < mHeight; i++) { 264 | for (int j = 0; j < mWidth; j++) { 265 | int c = imgData[i * mWidth + j]; 266 | a[idx++][k] = Color.red(c) + Color.green(c) + Color.blue(c); 267 | } 268 | } 269 | } 270 | 271 | DenseMatrix64F A = new DenseMatrix64F(a); 272 | CommonOps.transpose(A); 273 | SingularValueDecomposition svd = 274 | DecompositionFactory.svd(A.numRows, A.numCols, false, true, true); 275 | 276 | /* TODO: catch java.lang.OutOfMemoryError */ 277 | if (!svd.decompose(A)) { 278 | throw new RuntimeException("Decomposition failed"); 279 | } 280 | 281 | /* speeding up computation, SVD from A^TA instead of AA^T */ 282 | DenseMatrix64F EV = svd.getV(null, false); 283 | 284 | /* create RenderScript context */ 285 | RenderScript rs = RenderScript.create(getApplicationContext()); 286 | ScriptC_compute_normals cmpNormals = new ScriptC_compute_normals(rs); 287 | 288 | /* set params for the generator */ 289 | cmpNormals.set_width(mWidth); 290 | 291 | /* create allocation input to RenderScriptAvoid error since API 20: 292 | API 20+ only allows simple 1D allocations to be used with bind. */ 293 | Type dataType = 294 | new Builder(rs, Element.F32_4(rs)).setX(mWidth * mHeight).create(); 295 | Allocation allInData = Allocation.createTyped(rs, dataType); 296 | allInData.copyFromUnchecked(ArrayUtils.toFloatArray(EV.data)); 297 | 298 | /* create allocation for masked image */ 299 | Type maskType = new Builder(rs, Element.I32(rs)).setX(mWidth * mHeight).create(); 300 | Allocation allMask = Allocation.createTyped(rs, maskType); 301 | int[] mask = new int[mWidth * mHeight]; 302 | Mask.getPixels(mask, 0, mWidth, 0, 0, mWidth, mHeight); 303 | allMask.copyFrom(mask); 304 | 305 | /* bind pMask and pData pointer inside RenderScript */ 306 | cmpNormals.bind_pMask(allMask); 307 | 308 | /* create allocation for output */ 309 | Type normalsType = 310 | new Builder(rs, Element.F32_4(rs)).setX(mWidth * mHeight).create(); 311 | Allocation allOutNormals = Allocation.createTyped(rs, normalsType); 312 | 313 | cmpNormals.forEach_compute_normals(allInData, allOutNormals); 314 | 315 | /* save output from RenderScript */ 316 | float[] normals = new float[mWidth * mHeight * 4]; 317 | allOutNormals.copyTo(normals); 318 | 319 | return normals; 320 | } 321 | 322 | private void publishResult(String galleryId) { 323 | Intent publish = new Intent(NOTIFICATION); 324 | publish.putExtra(GALLERY_ID, galleryId); 325 | sendBroadcast(publish); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/rendering/TouchSurfaceView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.rendering; 7 | 8 | import android.content.Context; 9 | import android.graphics.PointF; 10 | import android.opengl.GLSurfaceView; 11 | import android.opengl.GLU; 12 | import android.view.MotionEvent; 13 | import android.view.ScaleGestureDetector; 14 | 15 | import java.nio.ByteBuffer; 16 | import java.nio.ByteOrder; 17 | import java.nio.FloatBuffer; 18 | 19 | import javax.microedition.khronos.egl.EGLConfig; 20 | import javax.microedition.khronos.opengles.GL10; 21 | 22 | import de.hsrm.objectify.math.Matrix4f; 23 | import de.hsrm.objectify.math.Quat4f; 24 | import de.hsrm.objectify.utils.ArcBall; 25 | 26 | /** 27 | * Creates a touchable surface view to move, scale and spin a rendered object on 28 | * the display screen. This class extends {@link GLSurfaceView} and makes use of 29 | * {@link ScaleGestureDetector} which was introduced with Android version 2.2 30 | * (Froyo). 31 | * 32 | * @author kwolf001 33 | */ 34 | public class TouchSurfaceView extends GLSurfaceView { 35 | private static final String TAG = "TouchSurfaceView"; 36 | private final Object matrixLock = new Object(); 37 | private Matrix4f lastRot = new Matrix4f(); 38 | private Matrix4f thisRot = new Matrix4f(); 39 | private float[] matrix = new float[16]; 40 | private ArcBall arcBall = new ArcBall(getWidth(), getHeight()); 41 | private int displayWidth, displayHeight; 42 | private ObjectModelRenderer renderer; 43 | private ScaleGestureDetector scaleDetector; 44 | private float mScaling = 1; 45 | 46 | public TouchSurfaceView(Context context, int width, int height) { 47 | super(context); 48 | this.displayWidth = width; 49 | this.displayHeight = height; 50 | 51 | arcBall.setBounds((float) width, (float) height); 52 | renderer = new ObjectModelRenderer(); 53 | setRenderer(renderer); 54 | setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 55 | scaleDetector = new ScaleGestureDetector(context, new SimpleScaleListener()); 56 | } 57 | 58 | @Override 59 | public boolean onTrackballEvent(MotionEvent event) { 60 | float TRACKBALL_SCALE_FACTOR = 36.0f; 61 | renderer.mAngleX += event.getX() * TRACKBALL_SCALE_FACTOR; 62 | renderer.mAngleY += event.getY() * TRACKBALL_SCALE_FACTOR; 63 | requestRender(); 64 | return true; 65 | } 66 | 67 | @Override 68 | public boolean onTouchEvent(MotionEvent event) { 69 | float x = event.getX(); 70 | float y = event.getY(); 71 | if (event.getPointerCount() > 1) { 72 | scaleDetector.onTouchEvent(event); 73 | requestRender(); 74 | return true; 75 | } 76 | switch (event.getAction()) { 77 | case MotionEvent.ACTION_DOWN: 78 | synchronized (matrixLock) { 79 | lastRot.copy(thisRot); 80 | } 81 | arcBall.click(new PointF(x, y)); 82 | break; 83 | case MotionEvent.ACTION_MOVE: 84 | Quat4f thisQuat = new Quat4f(); 85 | arcBall.drag(new PointF(x, y), thisQuat); 86 | synchronized (matrixLock) { 87 | thisRot.setRotation(thisQuat); 88 | thisRot = Matrix4f.mul(lastRot, thisRot); 89 | } 90 | requestRender(); 91 | break; 92 | } 93 | return true; 94 | } 95 | 96 | public void setObjectModel(ObjectModel objectModel) { 97 | renderer.setObjectModel(objectModel); 98 | } 99 | 100 | private class SimpleScaleListener 101 | extends ScaleGestureDetector.SimpleOnScaleGestureListener { 102 | public boolean onScale(ScaleGestureDetector detector) { 103 | mScaling *= detector.getScaleFactor(); 104 | if (mScaling < 0.5f) 105 | mScaling = 0.5f; 106 | if (mScaling > 100.5f) 107 | mScaling = 100.5f; 108 | invalidate(); 109 | return true; 110 | } 111 | } 112 | 113 | /** 114 | * This class takes care of the rendering of the model, implementing 115 | * {@link GLSurfaceView.Renderer} and adapting for our needs. 116 | * 117 | * @author kwolf001 118 | */ 119 | private class ObjectModelRenderer implements GLSurfaceView.Renderer { 120 | public float mAngleX; 121 | public float mAngleY; 122 | private ObjectModel mObjectModel = null; 123 | 124 | public ObjectModelRenderer() { 125 | lastRot.setIdentity(); 126 | thisRot.setIdentity(); 127 | thisRot.map(matrix); 128 | } 129 | 130 | @Override 131 | public void onSurfaceCreated(GL10 gl, EGLConfig config) { 132 | gl.glEnable(GL10.GL_TEXTURE_2D); 133 | gl.glShadeModel(GL10.GL_SMOOTH); 134 | gl.glClearColor(0.2f, 0.3f, 0.5f, 1.0f); 135 | gl.glClearDepthf(1.0f); 136 | gl.glEnable(GL10.GL_DEPTH_TEST); 137 | gl.glDepthFunc(GL10.GL_LEQUAL); 138 | gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 139 | gl.glEnable(GL10.GL_NORMALIZE); 140 | 141 | gl.glEnable(GL10.GL_LIGHTING); 142 | gl.glEnable(GL10.GL_LIGHT0); 143 | // LIGHT0 144 | // define ambient component of first light 145 | float[] light0Ambient = new float[] {0.9f, 0.9f, 0.9f, 1.0f}; 146 | ByteBuffer byteBuf = ByteBuffer.allocateDirect(light0Ambient.length * 4); 147 | byteBuf.order(ByteOrder.nativeOrder()); 148 | FloatBuffer light0AmbientBuffer = byteBuf.asFloatBuffer(); 149 | light0AmbientBuffer.put(light0Ambient); 150 | light0AmbientBuffer.rewind(); 151 | gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, light0AmbientBuffer); 152 | // define diffuse component of first light 153 | float[] light0Diffuse = new float[] {0.8f, 0.8f, 0.8f, 1.0f}; 154 | FloatBuffer light0diffuseBuffer = byteBuf.asFloatBuffer(); 155 | light0diffuseBuffer.put(light0Diffuse); 156 | light0diffuseBuffer.rewind(); 157 | gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, light0diffuseBuffer); 158 | // define specular component of first light 159 | float[] light0Specular = new float[] {0.8f, 0.8f, 0.8f, 1.0f}; 160 | FloatBuffer light0specularBuffer = byteBuf.asFloatBuffer(); 161 | light0specularBuffer.put(light0Specular); 162 | light0specularBuffer.rewind(); 163 | gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, light0specularBuffer); 164 | float[] light0Position = new float[] {1.0f, 3.0f, 3.0f, 1.0f}; 165 | FloatBuffer lightPosBuffer = byteBuf.asFloatBuffer(); 166 | lightPosBuffer.put(light0Position); 167 | lightPosBuffer.rewind(); 168 | gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosBuffer); 169 | gl.glLightf(GL10.GL_LIGHT0, GL10.GL_SPOT_CUTOFF, 65.0f); 170 | 171 | gl.glMatrixMode(GL10.GL_MODELVIEW); 172 | } 173 | 174 | @Override 175 | public void onDrawFrame(GL10 gl) { 176 | gl.glColor4f(0, 0, 0, 0); 177 | gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 178 | 179 | gl.glMatrixMode(GL10.GL_MODELVIEW); 180 | gl.glLoadIdentity(); 181 | GLU.gluLookAt(gl, 0, 0, -2, 0, 0, 0, 0, 1, 0); 182 | thisRot.map(matrix); 183 | gl.glMultMatrixf(matrix, 0); 184 | gl.glScalef(mScaling, mScaling, mScaling); 185 | 186 | if (mObjectModel != null) { 187 | gl.glScalef(mObjectModel.getLength(), mObjectModel.getLength(), 188 | mObjectModel.getLength()); 189 | gl.glTranslatef(-mObjectModel.getMiddlePoint()[0], 190 | -mObjectModel.getMiddlePoint()[1], -mObjectModel.getMiddlePoint()[2]); 191 | mObjectModel.draw(gl); 192 | } 193 | } 194 | 195 | @Override 196 | public void onSurfaceChanged(GL10 gl, int width, int height) { 197 | float ratio = (float) width / height; 198 | gl.glMatrixMode(GL10.GL_PROJECTION); 199 | gl.glLoadIdentity(); 200 | gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 201 | gl.glViewport(0, 0, width, height); 202 | } 203 | 204 | public void setObjectModel(ObjectModel objectModel) { 205 | mObjectModel = objectModel; 206 | requestRender(); 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/ArcBall.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | import android.graphics.PointF; 9 | 10 | import de.hsrm.objectify.math.Quat4f; 11 | import de.hsrm.objectify.math.Vector3f; 12 | 13 | /** 14 | * This class implements an arcball 16 | * rotation for our object viewer. 17 | */ 18 | public class ArcBall { 19 | private static final float Epsilon = 1.0e-5f; 20 | 21 | Vector3f StVec; 22 | Vector3f EnVec; 23 | float adjustWidth; 24 | float adjustHeight; 25 | int width, height; 26 | 27 | /** 28 | * creates new arcball 29 | * 30 | * @param width arcball width 31 | * @param height arcball height 32 | */ 33 | public ArcBall(int width, int height) { 34 | StVec = new Vector3f(); 35 | EnVec = new Vector3f(); 36 | setBounds(width, height); 37 | this.width = width; 38 | this.height = height; 39 | } 40 | 41 | /** 42 | * maps finger touch onto arcballs sphere 43 | * 44 | * @param point finger touch 45 | * @param vector vector from objects middlepoint to fingertouch 46 | */ 47 | public void mapToSphere(PointF point, Vector3f vector) { 48 | PointF tempPoint = new PointF(point.x, point.y); 49 | 50 | tempPoint.x = (tempPoint.x * this.adjustWidth) - 1.0f; 51 | tempPoint.y = (tempPoint.y * this.adjustHeight) - 1.0f; 52 | 53 | float length = (tempPoint.x * tempPoint.x) + (tempPoint.y * tempPoint.y); 54 | 55 | if (length > 1.0f) { 56 | float norm = (float) (1.0 / Math.sqrt(length)); 57 | vector.x = tempPoint.x * norm; 58 | vector.y = tempPoint.y * norm; 59 | vector.z = 0.0f; 60 | 61 | } else { 62 | vector.x = tempPoint.x; 63 | vector.y = tempPoint.y; 64 | vector.z = (float) Math.sqrt(1.0f - length); 65 | } 66 | } 67 | 68 | /** 69 | * sets new bounds for arcball 70 | * 71 | * @param width new width 72 | * @param height new height 73 | */ 74 | public void setBounds(float width, float height) { 75 | adjustWidth = 1.0f / ((width - 1.0f) * 0.5f); 76 | adjustHeight = 1.0f / ((height - 1.0f) * 0.5f); 77 | } 78 | 79 | public void click(PointF NewPt) { 80 | mapToSphere(NewPt, this.StVec); 81 | } 82 | 83 | /** 84 | * finger dragging while rotating with arcball 85 | * 86 | * @param NewPt new point 87 | * @param NewRot new rotation 88 | */ 89 | public void drag(PointF NewPt, Quat4f NewRot) { 90 | this.mapToSphere(NewPt, EnVec); 91 | 92 | if (NewRot != null) { 93 | Vector3f Perp = new Vector3f(); 94 | 95 | Vector3f.cross(StVec, EnVec, Perp); 96 | Perp = Vector3f.invert(Perp); 97 | 98 | if (Perp.length() > Epsilon) { 99 | NewRot.x = Perp.x; 100 | NewRot.y = Perp.y; 101 | NewRot.z = Perp.z; 102 | NewRot.w = Vector3f.dot(StVec, EnVec); 103 | } else { 104 | NewRot.x = NewRot.y = NewRot.z = NewRot.w = 0.0f; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | public class ArrayUtils { 9 | /** 10 | * Transform all array values from its current min, max range to a, b range 11 | * 12 | * @param array array to transform 13 | * @param a new minimum value of array 14 | * @param b new maximum value of array 15 | * @return transformed array 16 | */ 17 | public static float[] linearTransform(float[] array, float a, float b) { 18 | /* determine current min, max values */ 19 | float min = Float.MAX_VALUE; 20 | float max = Float.MIN_VALUE; 21 | for (int i = 0; i < array.length; i++) { 22 | if (array[i] < min) 23 | min = array[i]; 24 | if (array[i] > max) 25 | max = array[i]; 26 | } 27 | 28 | /* linear transformation of matrix values from [min,max] -> [a,b] */ 29 | for (int i = 0; i < array.length; i++) { 30 | array[i] = a + (b - a) * (array[i] - min) / (max - min); 31 | } 32 | 33 | return array; 34 | } 35 | 36 | public static float[] toFloatArray(double[] arr) { 37 | float[] floatArray = new float[arr.length]; 38 | for (int i = 0; i < arr.length; i++) { 39 | floatArray[i] = (float) arr[i]; 40 | } 41 | return floatArray; 42 | } 43 | 44 | public static float max(float[] values, int offset) { 45 | float maximum = values[offset]; 46 | for (int i = offset; i < values.length; i += 3) { 47 | if (values[i] > maximum) { 48 | maximum = values[i]; 49 | } 50 | } 51 | return maximum; 52 | } 53 | 54 | public static float max(float[] values) { 55 | float maximum = values[0]; 56 | for (int i = 1; i < values.length; i += 3) { 57 | if (values[i] > maximum) { 58 | maximum = values[i]; 59 | } 60 | } 61 | return maximum; 62 | } 63 | 64 | public static float min(float[] values, int offset) { 65 | float minimum = values[offset]; 66 | for (int i = offset; i < values.length; i += 3) { 67 | if (values[i] < minimum) { 68 | minimum = values[i]; 69 | } 70 | } 71 | return minimum; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/BitmapUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | import android.graphics.Color; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | 15 | import de.hsrm.objectify.camera.Constants; 16 | 17 | public class BitmapUtils { 18 | public static final double GS_RED = 0.299; 19 | public static final double GS_GREEN = 0.587; 20 | public static final double GS_BLUE = 0.114; 21 | 22 | public static Bitmap convertToGrayscale(Bitmap src) { 23 | int width = src.getWidth(); 24 | int height = src.getHeight(); 25 | int[] pixels = new int[width * height]; 26 | 27 | src.getPixels(pixels, 0, width, 0, 0, width, height); 28 | for (int i = 0; i < width * height; i++) { 29 | int p = pixels[i]; 30 | int intensity = getIntensity(p); 31 | pixels[i] = Color.rgb(intensity, intensity, intensity); 32 | } 33 | 34 | return Bitmap.createBitmap(pixels, width, height, src.getConfig()); 35 | } 36 | 37 | /** 38 | * Return histogram of grayscaled image 39 | */ 40 | public static int[] getHistogram(Bitmap bmp) { 41 | int[] histogram = new int[256]; 42 | int width = bmp.getWidth(); 43 | int height = bmp.getHeight(); 44 | int[] pixels = new int[width * height]; 45 | bmp.getPixels(pixels, 0, width, 0, 0, width, height); 46 | 47 | for (int i = 0; i < width * height; i++) { 48 | int c = Color.red(pixels[i]); 49 | histogram[c] += 1; 50 | } 51 | 52 | return histogram; 53 | } 54 | 55 | public static Bitmap binarize(Bitmap bmp) { 56 | int threshold = Math.round(getOtsuThreshold(bmp)); 57 | int width = bmp.getWidth(); 58 | int height = bmp.getHeight(); 59 | int[] pixels = new int[width * height]; 60 | bmp.getPixels(pixels, 0, width, 0, 0, width, height); 61 | 62 | for (int i = 0; i < width * height; i++) { 63 | int p = 0; 64 | if (Color.red(pixels[i]) > threshold) 65 | p = 255; 66 | pixels[i] = Color.rgb(p, p, p); 67 | } 68 | 69 | return Bitmap.createBitmap(pixels, width, height, bmp.getConfig()); 70 | } 71 | 72 | public static Bitmap convert(float[] normals, int width, int height) { 73 | int[] pixels = new int[width * height]; 74 | for (int i = 0; i < width * height; i++) { 75 | pixels[i] = Color.rgb((int) normals[4 * i + 0], (int) normals[4 * i + 1], 76 | (int) normals[4 * i + 2]); 77 | } 78 | 79 | return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); 80 | } 81 | 82 | private static int getOtsuThreshold(Bitmap bmp) { 83 | int[] histogram = getHistogram(bmp); 84 | int pixelCount = bmp.getWidth() * bmp.getHeight(); 85 | 86 | float sum = 0.0f; 87 | for (int i = 0; i < 256; i++) sum += i * histogram[i]; 88 | 89 | float sumB = 0.0f; 90 | int wB = 0; 91 | float varMax = 0.0f; 92 | int threshold = 0; 93 | 94 | for (int i = 0; i < 256; i++) { 95 | wB += histogram[i]; 96 | if (wB == 0) 97 | continue; 98 | int wF = pixelCount - wB; 99 | 100 | if (wF == 0) 101 | break; 102 | 103 | sumB += (float) (i * histogram[i]); 104 | float mB = sumB / wB; 105 | float mF = (sum - sumB) / wF; 106 | 107 | float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF); 108 | 109 | if (varBetween > varMax) { 110 | varMax = varBetween; 111 | threshold = i; 112 | } 113 | } 114 | 115 | return threshold; 116 | } 117 | 118 | private static int getIntensity(int rgb) { 119 | return (int) (Color.red(rgb) * BitmapUtils.GS_RED 120 | + Color.green(rgb) * BitmapUtils.GS_GREEN 121 | + Color.blue(rgb) * BitmapUtils.GS_BLUE); 122 | } 123 | 124 | private static int clamp(int value) { 125 | if (value > 255) 126 | return 255; 127 | if (value < 0) 128 | return 0; 129 | return value; 130 | } 131 | 132 | public static Bitmap subtract(Bitmap src, Bitmap ambient) { 133 | int width = src.getWidth(); 134 | int height = src.getHeight(); 135 | int[] srcPixels = new int[width * height]; 136 | int[] ambPixels = new int[width * height]; 137 | src.getPixels(srcPixels, 0, width, 0, 0, width, height); 138 | ambient.getPixels(ambPixels, 0, width, 0, 0, width, height); 139 | 140 | for (int i = 0; i < width * height; i++) { 141 | int srcPixel = getIntensity(srcPixels[i]); 142 | int otherPixel = getIntensity(ambPixels[i]); 143 | int c = clamp(srcPixel - otherPixel); 144 | srcPixels[i] = Color.rgb(c, c, c); 145 | } 146 | 147 | return Bitmap.createBitmap(srcPixels, width, height, src.getConfig()); 148 | } 149 | 150 | public static Bitmap openBitmap(String filepath) { 151 | return BitmapFactory.decodeFile(filepath); 152 | } 153 | 154 | public static void saveBitmap(Bitmap src, String dir, String filename) { 155 | File imageDirectory = new File(Storage.getExternalRootDirectory() + "/" + dir); 156 | imageDirectory.mkdirs(); 157 | File file = new File(imageDirectory, filename); 158 | if (file.exists()) 159 | file.delete(); 160 | try { 161 | FileOutputStream out = new FileOutputStream(file); 162 | src.compress(Constants.IMAGE_COMPRESS_FORMAT, 100, out); 163 | out.flush(); 164 | out.close(); 165 | } catch (Exception e) { 166 | e.printStackTrace(); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/CameraUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | import android.graphics.Bitmap; 9 | import android.graphics.Matrix; 10 | import android.hardware.Camera; 11 | import android.hardware.Camera.Size; 12 | 13 | import java.util.List; 14 | 15 | public class CameraUtils { 16 | public static Size determineTargetPictureSize( 17 | Camera.Parameters params, int desiredResolution) { 18 | List sizes = params.getSupportedPictureSizes(); 19 | Size targetSize = sizes.get(0); 20 | int delta = Integer.MAX_VALUE; 21 | 22 | for (Size size : sizes) { 23 | int diff = Math.abs(desiredResolution - pixelCount(size)); 24 | if (diff < delta) { 25 | targetSize = size; 26 | delta = diff; 27 | } 28 | } 29 | return targetSize; 30 | } 31 | 32 | public static Bitmap fixRotateMirrorImage(Bitmap src) { 33 | Matrix rotateRight = new Matrix(); 34 | float[] mirrorY = {-1, 0, 0, 0, 1, 0, 0, 0, 1}; 35 | Matrix matrixMirrorY = new Matrix(); 36 | matrixMirrorY.setValues(mirrorY); 37 | rotateRight.postConcat(matrixMirrorY); 38 | rotateRight.preRotate(270); 39 | final Bitmap rotMirrorImg = Bitmap.createBitmap( 40 | src, 0, 0, src.getWidth(), src.getHeight(), rotateRight, true); 41 | src.recycle(); 42 | 43 | return rotMirrorImg; 44 | } 45 | 46 | private static int pixelCount(Size size) { 47 | return size.width * size.height; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/Size.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | public class Size { 9 | public int width; 10 | public int height; 11 | 12 | public Size(int width, int height) { 13 | this.width = width; 14 | this.height = height; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return width + "x" + height; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/de/hsrm/objectify/utils/Storage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Objectify. Copyright (c) 2011-2016. Kai Wolf. All rights reserved. 3 | * Redistribution and use in source form with or without modification is not permitted. 4 | */ 5 | 6 | package de.hsrm.objectify.utils; 7 | 8 | import android.os.Environment; 9 | 10 | import java.io.File; 11 | import java.util.Random; 12 | 13 | public class Storage { 14 | private static final String DIRECTORY_NAME = "/Android/data/de.hsrm.objectify"; 15 | 16 | public static String getExternalRootDirectory() { 17 | return getDirectoryPath(DIRECTORY_NAME); 18 | } 19 | 20 | public static String getRandomName(int size) { 21 | char[] chars = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789" 22 | .toCharArray(); 23 | StringBuilder sb = new StringBuilder(); 24 | Random random = new Random(); 25 | for (int i = 0; i < size; i++) sb.append(chars[random.nextInt(chars.length)]); 26 | return sb.toString(); 27 | } 28 | 29 | private static String getDirectoryPath(String directory) { 30 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 31 | File dir = new File( 32 | Environment.getExternalStorageDirectory().getAbsolutePath() + directory); 33 | if (dir.mkdirs() || dir.exists()) { 34 | return dir.getAbsolutePath(); 35 | } else { 36 | throw new RuntimeException("Couldn't create external directory"); 37 | } 38 | } else { 39 | throw new RuntimeException("External Storage is currently not available"); 40 | } 41 | } 42 | 43 | public boolean isMounted() { 44 | String state = Environment.getExternalStorageState(); 45 | return Environment.MEDIA_MOUNTED.equals(state); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-hdpi/ic_action_export.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-mdpi/ic_action_export.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/camera_lighting_black.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/camera_lighting_black.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/camera_lighting_bottom.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/camera_lighting_bottom.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/camera_lighting_left.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/camera_lighting_left.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/camera_lighting_right.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/camera_lighting_right.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/camera_lighting_top.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/camera_lighting_top.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/lighting_mask.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-nodpi/lighting_mask.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-xhdpi/ic_action_export.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-xxhdpi/ic_action_export.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewProggie/Objectify/dc2fb18094989a9a788d67882cf187ce6d17122e/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_noise.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 17 | 18 | 24 | 25 |