0031620: Samples - update Android JNI sample to use AIS_ViewController
[occt.git] / samples / java / jniviewer / app / src / main / java / com / opencascade / jnisample / OcctJniView.java
1 // Copyright (c) 2014 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 package com.opencascade.jnisample;
15
16 import android.app.ActionBar.LayoutParams;
17 import android.content.Context;
18 import android.graphics.PointF;
19 import android.opengl.GLSurfaceView;
20 import android.util.AttributeSet;
21 import android.util.SparseArray;
22 import android.view.MotionEvent;
23 import android.widget.RelativeLayout;
24
25 import javax.microedition.khronos.egl.EGL10;
26 import javax.microedition.khronos.egl.EGLConfig;
27 import javax.microedition.khronos.egl.EGLContext;
28 import javax.microedition.khronos.egl.EGLDisplay;
29
30 //! OpenGL ES 2.0+ view.
31 //! Performs rendering in parallel thread.
32 class OcctJniView extends GLSurfaceView
33 {
34
35   // ! Default constructor.
36   public OcctJniView (Context      theContext,
37                       AttributeSet theAttrs)
38   {
39     super (theContext, theAttrs);
40
41     android.util.DisplayMetrics aDispInfo = theContext.getResources().getDisplayMetrics();
42     myScreenDensity = aDispInfo.density;
43
44     setPreserveEGLContextOnPause (true);
45     setEGLContextFactory (new ContextFactory());
46     setEGLConfigChooser  (new ConfigChooser());
47
48     RelativeLayout.LayoutParams aLParams = new RelativeLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
49     aLParams.addRule (RelativeLayout.ALIGN_TOP);
50
51     myRenderer = new OcctJniRenderer (this, myScreenDensity);
52     setRenderer (myRenderer);
53     setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY); // render on request to spare battery
54   }
55
56   //! Open file.
57   public void open (String thePath)
58   {
59     final String aPath = thePath;
60     queueEvent (new Runnable() { public void run() { myRenderer.open (aPath); }});
61     requestRender();
62   }
63
64   //! Create OpenGL ES 2.0+ context
65   private static class ContextFactory implements GLSurfaceView.EGLContextFactory
66   {
67     private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
68     public EGLContext createContext (EGL10      theEgl,
69                                      EGLDisplay theEglDisplay,
70                                      EGLConfig  theEglConfig)
71     {
72       if (theEglConfig == null)
73       {
74         return null;
75       }
76
77       // reset EGL errors stack
78       int anError = EGL10.EGL_SUCCESS;
79       while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) {}
80
81       int[]      anAttribs   = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
82       EGLContext aEglContext = theEgl.eglCreateContext (theEglDisplay, theEglConfig, EGL10.EGL_NO_CONTEXT, anAttribs);
83
84       while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
85       {
86         OcctJniLogger.postMessage ("Error: eglCreateContext() " + String.format ("0x%x", anError));
87       }
88       return aEglContext;
89     }
90
91     public void destroyContext (EGL10      theEgl,
92                                 EGLDisplay theEglDisplay,
93                                 EGLContext theEglContext)
94     {
95       theEgl.eglDestroyContext (theEglDisplay, theEglContext);
96     }
97   }
98
99   //! Search for RGB24 config with depth and stencil buffers
100   private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser
101   {
102     //! Reset EGL errors stack
103     private void popEglErrors (EGL10 theEgl)
104     {
105       int anError = EGL10.EGL_SUCCESS;
106       while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
107       {
108         OcctJniLogger.postMessage ("EGL Error: " + String.format ("0x%x", anError));
109       }
110     }
111
112     //! Auxiliary method to dump EGL configuration - for debugging purposes
113     @SuppressWarnings("unused")
114     private void printConfig (EGL10      theEgl,
115                               EGLDisplay theEglDisplay,
116                               EGLConfig  theEglConfig)
117     {
118       int[] THE_ATTRIBS =
119       {
120         EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_BLUE_SIZE, EGL10.EGL_GREEN_SIZE, EGL10.EGL_RED_SIZE, EGL10.EGL_DEPTH_SIZE, EGL10.EGL_STENCIL_SIZE,
121         EGL10.EGL_CONFIG_CAVEAT,
122         EGL10.EGL_CONFIG_ID,
123         EGL10.EGL_LEVEL,
124         EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH,
125         EGL10.EGL_NATIVE_RENDERABLE,  EGL10.EGL_NATIVE_VISUAL_ID,   EGL10.EGL_NATIVE_VISUAL_TYPE,
126         0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
127         EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS,
128         EGL10.EGL_SURFACE_TYPE,
129         EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE,
130         0x3039, 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGB, EGL10.EGL_BIND_TO_TEXTURE_RGBA,
131         0x303B, 0x303C, // EGL10.EGL_MIN_SWAP_INTERVAL, EGL10.EGL_MAX_SWAP_INTERVAL
132         EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE,
133         EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE,
134         0x3042 // EGL10.EGL_CONFORMANT
135       };
136       String[] THE_NAMES =
137       {
138         "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE", "EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE",
139         "EGL_CONFIG_CAVEAT",
140         "EGL_CONFIG_ID",
141         "EGL_LEVEL",
142         "EGL_MAX_PBUFFER_HEIGHT", "EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH",
143         "EGL_NATIVE_RENDERABLE",  "EGL_NATIVE_VISUAL_ID",   "EGL_NATIVE_VISUAL_TYPE",
144         "EGL_PRESERVED_RESOURCES",
145         "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS",
146         "EGL_SURFACE_TYPE",
147         "EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE",
148         "EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA",
149         "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL",
150         "EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE",
151         "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE",
152         "EGL_CONFORMANT"
153       };
154       int[] aValue = new int[1];
155       for (int anAttrIter = 0; anAttrIter < THE_ATTRIBS.length; ++anAttrIter)
156       {
157         int    anAttr = THE_ATTRIBS[anAttrIter];
158         String aName  = THE_NAMES  [anAttrIter];
159         if (theEgl.eglGetConfigAttrib (theEglDisplay, theEglConfig, anAttr, aValue))
160         {
161           OcctJniLogger.postMessage (String.format ("  %s: %d\n", aName, aValue[0]));
162         }
163         else
164         {
165           popEglErrors (theEgl);
166         }
167       }
168     }
169
170     //! Interface implementation
171     public EGLConfig chooseConfig (EGL10      theEgl,
172                                    EGLDisplay theEglDisplay)
173     {
174       int EGL_OPENGL_ES2_BIT = 4;
175       int[] aCfgAttribs =
176       {
177         EGL10.EGL_RED_SIZE,     8,
178         EGL10.EGL_GREEN_SIZE,   8,
179         EGL10.EGL_BLUE_SIZE,    8,
180         EGL10.EGL_ALPHA_SIZE,   0,
181         EGL10.EGL_DEPTH_SIZE,  24,
182         EGL10.EGL_STENCIL_SIZE, 8,
183         EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
184         EGL10.EGL_NONE
185       };
186
187       EGLConfig aConfigs[] = new EGLConfig[1];
188       int[]     aNbConfigs = new int[1];
189       if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
190        || aConfigs[0] == null)
191       {
192         aCfgAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer
193         popEglErrors (theEgl);
194         if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
195          || aConfigs[0] == null)
196         {
197           OcctJniLogger.postMessage ("Error: eglChooseConfig() has failed!");
198           return null;
199         }
200       }
201
202       //printConfig (theEgl, theEglDisplay, aConfigs[0]);
203       return aConfigs[0];
204     }
205   }
206
207   //! Callback to handle touch events
208   @Override public boolean onTouchEvent (MotionEvent theEvent)
209   {
210     final int aMaskedAction = theEvent.getActionMasked();
211     switch (aMaskedAction)
212     {
213       case MotionEvent.ACTION_DOWN:
214       case MotionEvent.ACTION_POINTER_DOWN:
215       {
216         final int aPointerIndex = theEvent.getActionIndex();
217         final int aPointerId    = theEvent.getPointerId (aPointerIndex);
218         final PointF aPnt = new PointF (theEvent.getX (aPointerIndex), theEvent.getY (aPointerIndex));
219
220         if (theEvent.getPointerCount() == 1)
221         {
222           mySelectPoint = aPnt;
223         }
224         else
225         {
226           mySelectPoint = null;
227         }
228
229         queueEvent (new Runnable() { public void run() { myRenderer.onAddTouchPoint (aPointerId, aPnt.x, aPnt.y); }});
230         break;
231       }
232       case MotionEvent.ACTION_MOVE:
233       {
234         for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter)
235         {
236           final int aPointerId = theEvent.getPointerId (aPntIter);
237           final PointF aPnt = new PointF (theEvent.getX (aPntIter), theEvent.getY (aPntIter));
238           queueEvent (new Runnable() { public void run() { myRenderer.onUpdateTouchPoint (aPointerId, aPnt.x, aPnt.y); }});
239         }
240         if (mySelectPoint != null)
241         {
242           final float aTouchThreshold = 5.0f * myScreenDensity;
243           final int aPointerIndex = theEvent.getActionIndex();
244           final PointF aDelta = new PointF (theEvent.getX (aPointerIndex) - mySelectPoint.x, theEvent.getY (aPointerIndex) - mySelectPoint.y);
245           if (Math.abs (aDelta.x) > aTouchThreshold || Math.abs (aDelta.y) > aTouchThreshold)
246           {
247             mySelectPoint = null;
248           }
249         }
250         break;
251       }
252       case MotionEvent.ACTION_UP:
253       case MotionEvent.ACTION_POINTER_UP:
254       case MotionEvent.ACTION_CANCEL:
255       {
256         if (mySelectPoint != null)
257         {
258           final float aSelX = mySelectPoint.x;
259           final float aSelY = mySelectPoint.y;
260           queueEvent (new Runnable() { public void run() { myRenderer.onSelectInViewer (aSelX, aSelY); }});
261           mySelectPoint = null;
262         }
263
264         final int aPointerIndex = theEvent.getActionIndex();
265         final int aPointerId    = theEvent.getPointerId (aPointerIndex);
266         final PointF aPnt = new PointF (theEvent.getX (aPointerIndex), theEvent.getY (aPointerIndex));
267         queueEvent (new Runnable() { public void run() { myRenderer.onRemoveTouchPoint (aPointerId); }});
268       }
269     }
270     requestRender();
271     return true;
272   }
273
274   //! Fit All
275   public void fitAll()
276   {
277     queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }});
278     requestRender();
279   }
280
281   //! Move camera
282   public void setProj (final OcctJniRenderer.TypeOfOrientation theProj)
283   {
284     queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }});
285     requestRender();
286   }
287
288   //! OCCT viewer
289   private OcctJniRenderer myRenderer = null;
290   private int    mySelectId = -1;
291   private PointF mySelectPoint = null;
292   private float  myScreenDensity = 1.0f;
293
294 }