OSDN Git Service

5bde5f4542d500bdccd136e279ea2cb742ddb53e
[nyartoolkit-and/nyartoolkit-and.git] / src / jp / androidgroup / nyartoolkit / NyARToolkitAndroidActivity.java
1 /*
2  * PROJECT: NyARToolkit for Android SDK
3  * --------------------------------------------------------------------------------
4  * This work is based on the original ARToolKit developed by
5  *   Hirokazu Kato
6  *   Mark Billinghurst
7  *   HITLab, University of Washington, Seattle
8  * http://www.hitl.washington.edu/artoolkit/
9  *
10  * NyARToolkit for Android SDK
11  *   Copyright (C)2010 NyARToolkit for Android team
12  *   Copyright (C)2010 R.Iizuka(nyatla)
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  *
27  * For further information please contact.
28  *  http://sourceforge.jp/projects/nyartoolkit-and/
29  *
30  * This work is based on the NyARToolKit developed by
31  *  R.Iizuka (nyatla)
32  *    http://nyatla.jp/nyatoolkit/
33  *
34  * contributor(s)
35  *  Atsuo Igarashi
36  */
37
38 package jp.androidgroup.nyartoolkit;
39
40 import java.io.InputStream;
41 import java.util.ArrayList;
42
43 import com.android.camera.CameraHardwareException;
44 import com.android.camera.CameraHolder;
45
46 import min3d.Shared;
47 import min3d.animation.AnimationObject3d;
48 import min3d.core.Renderer;
49 import min3d.core.Scene;
50 import min3d.parser.IParser;
51 import min3d.parser.Parser;
52 import min3d.vos.Light;
53 import min3d.vos.TextureVo;
54
55 import android.app.Activity;
56 import android.app.Dialog;
57 import android.app.ProgressDialog;
58 import android.content.Intent;
59 import android.content.SharedPreferences;
60 import android.content.res.Configuration;
61 import android.content.res.Resources;
62 import android.graphics.Bitmap;
63 import android.graphics.Matrix;
64 import android.graphics.PixelFormat;
65 import android.hardware.Camera;
66 import android.hardware.Camera.PreviewCallback;
67 import android.media.MediaPlayer;
68 import android.opengl.GLSurfaceView;
69 import android.os.Build;
70 import android.os.Bundle;
71 import android.os.Handler;
72 import android.os.Message;
73 import android.preference.PreferenceManager;
74 import android.util.Log;
75 import android.view.MotionEvent;
76 import android.view.OrientationEventListener;
77 import android.view.SurfaceHolder;
78 import android.view.SurfaceView;
79 import android.view.View;
80 import android.view.Window;
81 import android.view.WindowManager;
82 import android.view.ViewGroup.LayoutParams;
83 import android.widget.FrameLayout;
84 import android.widget.ImageView;
85
86 //public class NyARToolkitAndroidActivity extends Activity implements View.OnClickListener, SurfaceHolder.Callback {
87 public class NyARToolkitAndroidActivity extends Activity implements View.OnClickListener, SurfaceHolder.Callback, min3d.interfaces.ISceneController {
88
89         public static final String TAG = "NyARToolkitAndroid";
90
91         private static final int CROP_MSG = 1;
92         private static final int FIRST_TIME_INIT = 2;
93         private static final int RESTART_PREVIEW = 3;
94         private static final int CLEAR_SCREEN_DELAY = 4;
95     private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 5;
96     public static final int SHOW_LOADING = 6;
97     public static final int HIDE_LOADING = 7;
98
99     private static final int SCREEN_DELAY = 2 * 60 * 1000;
100     
101         private android.hardware.Camera.Parameters mParameters;
102
103     private OrientationEventListener mOrientationListener;
104     private int mLastOrientation = 0;
105     private SharedPreferences mPreferences;
106         
107     private static final int IDLE = 1;
108     private static final int SNAPSHOT_IN_PROGRESS = 2;
109         
110         private int mStatus = IDLE;
111
112     private android.hardware.Camera mCameraDevice;
113     private SurfaceView mSurfaceView;
114     private SurfaceHolder mSurfaceHolder = null;
115     private boolean mStartPreviewFail = false;
116
117     private GLSurfaceView mGLSurfaceView = null;
118         // Renderer for metasequoia model
119 //    private ModelRenderer mRenderer;
120         // Renderer of min3d
121     private Renderer mRenderer;
122
123         private boolean mPreviewing;
124         private boolean mPausing;
125         private boolean mFirstTimeInitialized;
126
127         private Handler mHandler = new MainHandler();
128
129         private PreviewCallback mPreviewCallback = new PreviewCallback();
130
131         private ARToolkitDrawer arToolkitDrawer = null;
132
133         private MediaPlayer mMediaPlayer = null;
134
135         /** This Handler is used to post message back onto the main thread of the application */
136         private class MainHandler extends Handler {
137                 @Override
138                 public void handleMessage(Message msg) {
139                         switch (msg.what) {
140                 case RESTART_PREVIEW: {
141                         restartPreview();
142                         break;
143                 }
144
145                                 case CLEAR_SCREEN_DELAY: {
146                                         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
147                                         break;
148                                 }
149
150                 case FIRST_TIME_INIT: {
151                     initializeFirstTime();
152                     break;
153                 }
154
155                 case SHOW_LOADING: {
156                                         showDialog(DIALOG_LOADING);
157                                         break;
158                                 }
159                                 case HIDE_LOADING: {
160                                         try {
161                                                 dismissDialog(DIALOG_LOADING);
162                                                 removeDialog(DIALOG_LOADING);
163                                         } catch (IllegalArgumentException e) {
164                                         }
165                                         break;
166                                 }
167                         }
168                 }
169         }
170
171         private static final int DIALOG_LOADING = 0;
172
173         @Override
174         protected Dialog onCreateDialog(int id) {
175                 switch (id) {
176                 case DIALOG_LOADING: {
177                         ProgressDialog dialog = new ProgressDialog(this);
178                         dialog.setMessage("Loading ...");
179                         // dialog.setIndeterminate(true);
180                         dialog.setCancelable(false);
181                         dialog.getWindow().setFlags
182                                 (WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
183                                  WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
184                         return dialog;
185                 }
186                 default:
187                         return super.onCreateDialog(id);
188                 }
189         }
190
191         public static int roundOrientation(int orientationInput) {
192 //              Log.d("roundOrientation", "orientationInput:" + orientationInput);
193                 int orientation = orientationInput;
194                 if (orientation == -1)
195                         orientation = 0;
196
197                 orientation = orientation % 360;
198                 int retVal;
199                 if (orientation < (0*90) + 45) {
200                         retVal = 0;
201                 } else if (orientation < (1*90) + 45) {
202                         retVal = 90;
203                 } else if (orientation < (2*90) + 45) {
204                         retVal = 180;
205                 } else if (orientation < (3*90) + 45) {
206                         retVal = 270;
207                 } else {
208                         retVal = 0;
209                 }
210
211                 return retVal;
212         }
213
214     // Snapshots can only be taken after this is called. It should be called
215     // once only. We could have done these things in onCreate() but we want to
216     // make preview screen appear as soon as possible.
217     private void initializeFirstTime() {
218         if (mFirstTimeInitialized) return;
219
220         Log.d(TAG, "initializeFirstTime");
221
222         // Create orientation listenter. This should be done first because it
223         // takes some time to get first orientation.
224         mOrientationListener =
225                 new OrientationEventListener(this) {
226             @Override
227             public void onOrientationChanged(int orientation) {
228                 // We keep the last known orientation. So if the user
229                 // first orient the camera then point the camera to
230                 // floor/sky, we still have the correct orientation.
231                 if (orientation != ORIENTATION_UNKNOWN) {
232                     orientation += 90;
233                 }
234                 orientation = roundOrientation(orientation);
235                 if (orientation != mLastOrientation) {
236                     mLastOrientation = orientation;
237                 }
238             }
239         };
240         mOrientationListener.enable();
241
242         mFirstTimeInitialized = true;
243         
244         changeGLSurfaceViewState();
245     }
246
247     // If the activity is paused and resumed, this method will be called in
248     // onResume.
249     private void initializeSecondTime() {
250                 Log.d(TAG, "initializeSecondTime");
251
252                 // Start orientation listener as soon as possible because it takes
253         // some time to get first orientation.
254         mOrientationListener.enable();
255         
256         changeGLSurfaceViewState();
257     }
258
259     /**
260      * Callback interface used to deliver copies of preview frames as they are displayed.
261      */
262     private final class PreviewCallback
263             implements android.hardware.Camera.PreviewCallback {
264
265                 @Override
266                 public void onPreviewFrame(byte [] data, Camera camera) {
267                         Log.d(TAG, "PreviewCallback.onPreviewFrame");
268
269                         if (mPausing) {
270                 return;
271             }
272
273                         if(data != null) {
274                                 Log.d(TAG, "data exist");
275
276                                 if (arToolkitDrawer != null)
277                                         arToolkitDrawer.draw(data);
278                                 
279                         } else {
280                                 try {
281                                         // The measure against over load.
282                                         Thread.sleep(500);
283                                 } catch (InterruptedException e) {
284                                         ;
285                                 }
286                         }
287                 restartPreview();
288                 }
289         }
290
291         /** Called with the activity is first created. */
292         @Override
293         public void onCreate(Bundle icicle) {
294                 super.onCreate(icicle);
295
296                 // Renderer for metasequoia model
297 //              String[] modelName = new String[2];
298 //              modelName[0] = "droid.mqo";
299 //              modelName[1] = "miku01.mqo";
300 //              float[] modelScale = new float[] {0.01f, 0.03f};
301 //              mRenderer = new ModelRenderer(getAssets(), modelName, modelScale);
302 //              mRenderer.setMainHandler(mHandler);
303
304                 // Renderer of min3d
305                 _initSceneHander = new Handler();
306                 _updateSceneHander = new Handler();
307
308                 //
309                 // These 4 lines are important.
310                 //
311                 Shared.context(this);
312                 scene = new Scene(this);
313                 scene.backgroundTransparent(true);
314                 mRenderer = new Renderer(scene);
315                 Shared.renderer(mRenderer);
316
317                 requestWindowFeature(Window.FEATURE_PROGRESS);
318
319                 Window win = getWindow();
320                 win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
321                 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
322
323         setContentView(R.layout.main);
324         mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview);
325                 mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
326
327                 mSurfaceView.setKeepScreenOn(true);
328
329         // don't set mSurfaceHolder here. We have it set ONLY within
330         // surfaceChanged / surfaceDestroyed, other parts of the code
331         // assume that when it is set, the surface is also set.
332         SurfaceHolder holder = mSurfaceView.getHolder();
333         holder.addCallback(this);
334         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
335         }
336
337     private void changeGLSurfaceViewState() {
338         // If the camera resumes behind the lock screen, the orientation
339         // will be portrait. That causes OOM when we try to allocation GPU
340         // memory for the GLSurfaceView again when the orientation changes. So,
341         // we delayed initialization of GLSurfaceView until the orientation
342         // becomes landscape.
343         Configuration config = getResources().getConfiguration();
344         if (config.orientation == Configuration.ORIENTATION_LANDSCAPE
345                 && !mPausing && mFirstTimeInitialized) {
346             if (mGLSurfaceView == null) initializeGLSurfaceView();
347         } else if (mGLSurfaceView != null) {
348             finalizeGLSurfaceView();
349         }
350     }
351
352     private void initializeGLSurfaceView() {
353
354                 // init ARToolkit.
355         if (arToolkitDrawer == null) {
356                         InputStream camePara = getResources().openRawResource(R.raw.camera_para);
357                         int[] width = new int[2];
358                         for (int i = 0; i < 2; i++) {
359                                 width[i] = 80;
360                         }
361                         ArrayList<InputStream> patt = new ArrayList<InputStream>();
362                         patt.add(getResources().openRawResource(R.raw.patthiro));
363                         patt.add(getResources().openRawResource(R.raw.pattkanji));
364                         arToolkitDrawer = new ARToolkitDrawer(camePara, width, patt, mRenderer);
365
366
367 //                      mMediaPlayer = MediaPlayer.create(this, R.raw.miku_voice);
368 //                      mMediaPlayer.setLooping(true);
369 //              mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
370 //                  public void onPrepared(MediaPlayer mediaplayer) {
371 //                              arToolkitDrawer.setMediaPlayer(mediaplayer);
372 //                  }
373 //              });
374         }
375
376                 FrameLayout frame = (FrameLayout) findViewById(R.id.frame);
377                 mGLSurfaceView = new GLSurfaceView(this);
378                 mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
379                 mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
380                 mGLSurfaceView.setZOrderOnTop(true);
381                 mGLSurfaceView.setRenderer(mRenderer);
382         frame.addView(mGLSurfaceView);
383     }
384
385     private void finalizeGLSurfaceView() {
386                 FrameLayout frame = (FrameLayout) findViewById(R.id.frame);
387         frame.removeView(mGLSurfaceView);
388         mGLSurfaceView = null;
389
390         if (mMediaPlayer != null)
391                 mMediaPlayer.release();
392         mMediaPlayer = null;
393
394         arToolkitDrawer = null;
395     }
396
397         @Override
398         protected void onDestroy() {
399                 super.onDestroy();
400         }
401
402         @Override
403         public void onStart() {
404                 super.onStart();
405         }
406
407         @Override
408         public void onStop() {
409                 super.onStop();
410         }
411
412         @Override
413         public void onClick(View v) {
414                 ;;
415         }
416
417         @Override
418         public void onResume() {
419                 super.onResume();
420                 Log.d(TAG, "onResume");
421
422                 mPausing = false;
423
424         // Start the preview if it is not started.
425         if (!mPreviewing && !mStartPreviewFail && (mSurfaceHolder != null)) {
426             try {
427                 startPreview();
428             } catch (Exception e) {
429                 showCameraErrorAndFinish();
430                 return;
431             }
432         }
433
434         if (mSurfaceHolder != null) {
435             // If first time initialization is not finished, put it in the
436             // message queue.
437             if (!mFirstTimeInitialized) {
438                 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
439             } else {
440                 initializeSecondTime();
441             }
442         }
443         keepScreenOnAwhile();
444         }
445
446         @Override
447         protected void onPause() {
448                 Log.d(TAG, "onPause");
449                 mPausing = true;
450                 stopPreview();
451         // Close the camera now because other activities may need to use it.
452         closeCamera();
453         resetScreenOn();
454         changeGLSurfaceViewState();
455
456         if (mFirstTimeInitialized) {
457             mOrientationListener.disable();
458         }
459
460         // Remove the messages in the event queue.
461         mHandler.removeMessages(RESTART_PREVIEW);
462         mHandler.removeMessages(FIRST_TIME_INIT);
463
464                 super.onPause();
465         }
466
467         @Override
468         protected void onActivityResult(int requestCode, int resultCode, Intent data) {
469                 switch (requestCode) {
470                         case CROP_MSG: {
471                                 Intent intent = new Intent();
472                                 if (data != null) {
473                                         Bundle extras = data.getExtras();
474                                         if (extras != null) {
475                                                 intent.putExtras(extras);
476                                         }
477                                 }
478                                 setResult(resultCode, intent);
479                                 finish();
480                                 break;
481                         }
482                 }
483         }
484
485         @Override
486         public boolean onTouchEvent(MotionEvent event) {
487                 switch (event.getAction()) {
488                         case MotionEvent.ACTION_DOWN:
489                                 break;
490
491                         case MotionEvent.ACTION_MOVE:
492                                 break;
493
494                         case MotionEvent.ACTION_UP:
495                                 break;
496                 }
497                 return true;
498         }
499
500         @Override
501         public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
502                 Log.d(TAG, "surfaceChanged");
503
504                 // Make sure we have a surface in the holder before proceeding.
505         if (holder.getSurface() == null) {
506             Log.d(TAG, "holder.getSurface() == null");
507             return;
508         }
509
510         // We need to save the holder for later use, even when the mCameraDevice
511         // is null. This could happen if onResume() is invoked after this
512         // function.
513         mSurfaceHolder = holder;
514
515         // The mCameraDevice will be null if it fails to connect to the camera
516         // hardware. In this case we will show a dialog and then finish the
517         // activity, so it's OK to ignore it.
518         if (mCameraDevice == null) {
519
520                 /*
521                  * To reduce startup time, we start the preview in another thread.
522                  * We make sure the preview is started at the end of surfaceChanged.
523                  */
524                 Thread startPreviewThread = new Thread(new Runnable() {
525                         public void run() {
526                                 try {
527                                         mStartPreviewFail = false;
528                                         startPreview();
529                                 } catch (Exception e) {
530                                         // In eng build, we throw the exception so that test tool
531                                         // can detect it and report it
532                                         if ("eng".equals(Build.TYPE)) {
533                                                 throw new RuntimeException(e);
534                                         }
535                                         mStartPreviewFail = true;
536                                 }
537                         }
538                 });
539                 startPreviewThread.start();
540
541                 // Make sure preview is started.
542                 try {
543                         startPreviewThread.join();
544                         if (mStartPreviewFail) {
545                                 showCameraErrorAndFinish();
546                                 return;
547                         }
548                 } catch (InterruptedException ex) {
549                         // ignore
550                 }
551         }
552
553                 // Sometimes surfaceChanged is called after onPause.
554                 // Ignore it.
555                 if (mPausing || isFinishing()) return;
556
557         // If first time initialization is not finished, send a message to do
558         // it later. We want to finish surfaceChanged as soon as possible to let
559         // user see preview first.
560         if (!mFirstTimeInitialized) {
561             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
562         } else {
563             initializeSecondTime();
564         }
565         }
566
567         @Override
568         public void surfaceCreated(SurfaceHolder holder) {
569         }
570
571         @Override
572         public void surfaceDestroyed(SurfaceHolder holder) {
573                 stopPreview();
574                 mSurfaceHolder = null;
575         }
576         
577         private void closeCamera() {
578         if (mCameraDevice != null) {
579             CameraHolder.instance().release();
580             mCameraDevice = null;
581             mPreviewing = false;
582         }
583         }
584         
585     private void ensureCameraDevice() throws CameraHardwareException {
586         if (mCameraDevice == null) {
587             mCameraDevice = CameraHolder.instance().open();
588         }
589         }
590
591     private void showCameraErrorAndFinish() {
592         Resources ress = getResources();
593         com.android.camera.Util.showFatalErrorAndFinish(NyARToolkitAndroidActivity.this,
594                 ress.getString(R.string.camera_error_title),
595                 ress.getString(R.string.cannot_connect_camera));
596     }
597         
598         public void restartPreview() {
599                 Log.d(TAG, "restartPreview");
600         try {
601             startPreview();
602         } catch (CameraHardwareException e) {
603             showCameraErrorAndFinish();
604             return;
605         }
606         }
607         
608         private void setPreviewDisplay(SurfaceHolder holder) {
609                 try {
610                         mCameraDevice.setPreviewDisplay(holder);
611                 } catch (Throwable ex) {
612                         closeCamera();
613                         throw new RuntimeException("setPreviewDisplay failed", ex);
614                 }
615         }
616         
617     private void startPreview() throws CameraHardwareException {
618         if (mPausing || isFinishing()) return;
619
620         ensureCameraDevice();
621                 
622                 // If we're previewing already, stop the preview first (this will blank
623                 // the screen).
624         // FIXME: don't stop for avoiding blank screen.
625 //        if (mPreviewing) stopPreview();
626
627         setPreviewDisplay(mSurfaceHolder);
628         if (!mPreviewing)
629                 setCameraParameters();
630
631                 mCameraDevice.setOneShotPreviewCallback(mPreviewCallback);
632
633                 try {
634                         Log.v(TAG, "startPreview");
635                         mCameraDevice.startPreview();
636                 } catch (Throwable ex) {
637                         closeCamera();
638                         throw new RuntimeException("startPreview failed", ex);
639                 }
640                 mPreviewing = true;
641                 mStatus = IDLE;
642         }
643         
644         private void stopPreview() {
645                 if (mCameraDevice != null && mPreviewing) {
646                         Log.v(TAG, "stopPreview");
647                 mCameraDevice.setOneShotPreviewCallback(null);
648                         mCameraDevice.stopPreview();
649                 }
650                 mPreviewing = false;
651         }
652
653         private void setCameraParameters() {
654                 mParameters = mCameraDevice.getParameters();
655                 
656                 mParameters.setPreviewSize(320, 240);
657                 
658                 mCameraDevice.setParameters(mParameters);
659         }
660
661     private void resetScreenOn() {
662         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
663         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
664     }
665
666     private void keepScreenOnAwhile() {
667         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
668         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
669         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
670     }
671
672 //----------------------- for min3d ------------------------
673         public Scene scene;
674
675         protected Handler _initSceneHander;
676         protected Handler _updateSceneHander;
677
678         final Runnable _initSceneRunnable = new Runnable()
679         {
680             public void run() {
681                 onInitScene();
682             }
683         };
684
685         final Runnable _updateSceneRunnable = new Runnable()
686         {
687             public void run() {
688                 onUpdateScene();
689             }
690         };
691
692         /**
693          * Instantiation of Object3D's, setting their properties, and adding Object3D's
694          * to the scene should be done here. Or any point thereafter.
695          *
696          * Note that this method is always called after GLCanvas is created, which occurs
697          * not only on Activity.onCreate(), but on Activity.onResume() as well.
698          * It is the user's responsibility to build the logic to restore state on-resume.
699          *
700          * @see min3d.interfaces.ISceneController#initScene()
701          */
702         public void initScene()
703         {
704                 scene.lights().add(new Light());
705                 scene.camera().frustum.zFar(10000.0f);
706                 //scene.camera().frustum.shortSideLength(0.77f);
707
708                 IParser parser;
709                 AnimationObject3d animationObject3d = null;
710
711                 parser = Parser.createParser(Parser.Type.MD2,
712                                 getResources(), "jp.androidgroup.nyartoolkit:raw/droid", false);
713                 parser.parse();
714
715                 animationObject3d = parser.getParsedAnimationObject();
716                 animationObject3d.rotation().z = -90.0f;
717                 animationObject3d.scale().x = animationObject3d.scale().y = animationObject3d.scale().z = 1.0f;
718                 scene.addChild(animationObject3d);
719                 animationObject3d.setFps(30);
720
721                 parser = Parser.createParser(Parser.Type.MD2,
722                                 getResources(), "jp.androidgroup.nyartoolkit:raw/droidr", false);
723                 parser.parse();
724
725                 animationObject3d = parser.getParsedAnimationObject();
726                 animationObject3d.rotation().z = -90.0f;
727                 animationObject3d.scale().x = animationObject3d.scale().y = animationObject3d.scale().z = 1.0f;
728                 scene.addChild(animationObject3d);
729                 animationObject3d.setFps(90);
730         }
731
732         /**
733          * All manipulation of scene and Object3D instance properties should go here.
734          * Gets called on every frame, right before drawing.
735          *
736          * @see min3d.interfaces.ISceneController#updateScene()
737          */
738         public void updateScene()
739         {
740         }
741
742         /**
743          * Called _after_ scene init (ie, after initScene).
744          * Unlike initScene(), is thread-safe.
745          */
746         public void onInitScene()
747         {
748         }
749
750         /**
751          * Called _after_ scene init (ie, after initScene).
752          * Unlike initScene(), is thread-safe.
753          */
754         public void onUpdateScene()
755         {
756         }
757
758         /**
759          * @see min3d.interfaces.ISceneController#getInitSceneHandler()
760          */
761         public Handler getInitSceneHandler()
762         {
763                 return _initSceneHander;
764         }
765
766         /**
767          * @see min3d.interfaces.ISceneController#getUpdateSceneHandler()
768          */
769         public Handler getUpdateSceneHandler()
770         {
771                 return _updateSceneHander;
772         }
773
774         /**
775          * @see min3d.interfaces.ISceneController#getInitSceneRunnable()
776          */
777         public Runnable getInitSceneRunnable()
778         {
779                 return _initSceneRunnable;
780         }
781
782         /**
783          * @see min3d.interfaces.ISceneController#getUpdateSceneRunnable()
784          */
785         public Runnable getUpdateSceneRunnable()
786         {
787                 return _updateSceneRunnable;
788         }
789 //----------------------- for min3d ------------------------
790 }
791