1 // Copyright (c) 2014 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
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.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 package com.opencascade.jnisample;
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;
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;
30 //! OpenGL ES 2.0+ view.
31 //! Performs rendering in parallel thread.
32 class OcctJniView extends GLSurfaceView
35 // ! Default constructor.
36 public OcctJniView (Context theContext,
37 AttributeSet theAttrs)
39 super (theContext, theAttrs);
41 setPreserveEGLContextOnPause (true);
42 setEGLContextFactory (new ContextFactory());
43 setEGLConfigChooser (new ConfigChooser());
45 RelativeLayout.LayoutParams aLParams = new RelativeLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
46 aLParams.addRule (RelativeLayout.ALIGN_TOP);
48 myRenderer = new OcctJniRenderer();
49 setRenderer (myRenderer);
53 public void open (String thePath)
55 final String aPath = thePath;
56 queueEvent (new Runnable() { public void run() { myRenderer.open (aPath); }});
59 //! Create OpenGL ES 2.0+ context
60 private static class ContextFactory implements GLSurfaceView.EGLContextFactory
62 private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
63 public EGLContext createContext (EGL10 theEgl,
64 EGLDisplay theEglDisplay,
65 EGLConfig theEglConfig)
67 if (theEglConfig == null)
72 // reset EGL errors stack
73 int anError = EGL10.EGL_SUCCESS;
74 while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) {}
76 int[] anAttribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
77 EGLContext aEglContext = theEgl.eglCreateContext (theEglDisplay, theEglConfig, EGL10.EGL_NO_CONTEXT, anAttribs);
79 while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
81 OcctJniLogger.postMessage ("Error: eglCreateContext() " + String.format ("0x%x", anError));
86 public void destroyContext (EGL10 theEgl,
87 EGLDisplay theEglDisplay,
88 EGLContext theEglContext)
90 theEgl.eglDestroyContext (theEglDisplay, theEglContext);
94 //! Search for RGB24 config with depth and stencil buffers
95 private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser
97 //! Reset EGL errors stack
98 private void popEglErrors (EGL10 theEgl)
100 int anError = EGL10.EGL_SUCCESS;
101 while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
103 OcctJniLogger.postMessage ("EGL Error: " + String.format ("0x%x", anError));
107 //! Auxiliary method to dump EGL configuration - for debugging purposes
108 @SuppressWarnings("unused")
109 private void printConfig (EGL10 theEgl,
110 EGLDisplay theEglDisplay,
111 EGLConfig theEglConfig)
115 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,
116 EGL10.EGL_CONFIG_CAVEAT,
119 EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH,
120 EGL10.EGL_NATIVE_RENDERABLE, EGL10.EGL_NATIVE_VISUAL_ID, EGL10.EGL_NATIVE_VISUAL_TYPE,
121 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
122 EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS,
123 EGL10.EGL_SURFACE_TYPE,
124 EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE,
125 0x3039, 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGB, EGL10.EGL_BIND_TO_TEXTURE_RGBA,
126 0x303B, 0x303C, // EGL10.EGL_MIN_SWAP_INTERVAL, EGL10.EGL_MAX_SWAP_INTERVAL
127 EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE,
128 EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE,
129 0x3042 // EGL10.EGL_CONFORMANT
133 "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE", "EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE",
137 "EGL_MAX_PBUFFER_HEIGHT", "EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH",
138 "EGL_NATIVE_RENDERABLE", "EGL_NATIVE_VISUAL_ID", "EGL_NATIVE_VISUAL_TYPE",
139 "EGL_PRESERVED_RESOURCES",
140 "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS",
142 "EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE",
143 "EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA",
144 "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL",
145 "EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE",
146 "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE",
149 int[] aValue = new int[1];
150 for (int anAttrIter = 0; anAttrIter < THE_ATTRIBS.length; ++anAttrIter)
152 int anAttr = THE_ATTRIBS[anAttrIter];
153 String aName = THE_NAMES [anAttrIter];
154 if (theEgl.eglGetConfigAttrib (theEglDisplay, theEglConfig, anAttr, aValue))
156 OcctJniLogger.postMessage (String.format (" %s: %d\n", aName, aValue[0]));
160 popEglErrors (theEgl);
165 //! Interface implementation
166 public EGLConfig chooseConfig (EGL10 theEgl,
167 EGLDisplay theEglDisplay)
169 int EGL_OPENGL_ES2_BIT = 4;
172 EGL10.EGL_RED_SIZE, 8,
173 EGL10.EGL_GREEN_SIZE, 8,
174 EGL10.EGL_BLUE_SIZE, 8,
175 EGL10.EGL_ALPHA_SIZE, 0,
176 EGL10.EGL_DEPTH_SIZE, 24,
177 EGL10.EGL_STENCIL_SIZE, 8,
178 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
182 EGLConfig aConfigs[] = new EGLConfig[1];
183 int[] aNbConfigs = new int[1];
184 if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
185 || aConfigs[0] == null)
187 aCfgAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer
188 popEglErrors (theEgl);
189 if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
190 || aConfigs[0] == null)
192 OcctJniLogger.postMessage ("Error: eglChooseConfig() has failed!");
197 //printConfig (theEgl, theEglDisplay, aConfigs[0]);
202 //! Callback to handle touch events
203 @Override public boolean onTouchEvent (MotionEvent theEvent)
205 int aPointerIndex = theEvent.getActionIndex();
206 int aPointerId = theEvent.getPointerId (aPointerIndex);
207 int aMaskedAction = theEvent.getActionMasked();
208 switch (aMaskedAction)
210 case MotionEvent.ACTION_DOWN:
211 case MotionEvent.ACTION_POINTER_DOWN:
213 PointF aPntLast = null;
214 if (myActivePointers.size() >= 1)
216 aPntLast = myActivePointers.get (myActivePointers.keyAt (0));
219 final PointF aPnt = new PointF();
220 aPnt.x = theEvent.getX (aPointerIndex);
221 aPnt.y = theEvent.getY (aPointerIndex);
222 myActivePointers.put (aPointerId, aPnt);
224 switch (myActivePointers.size())
228 final int aStartX = (int )aPnt.x;
229 final int aStartY = (int )aPnt.y;
230 queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
235 myPanFrom.x = (aPntLast.x + aPnt.x) * 0.5f;
236 myPanFrom.y = (aPntLast.y + aPnt.y) * 0.5f;
243 case MotionEvent.ACTION_MOVE:
245 for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter)
247 PointF aPnt = myActivePointers.get (theEvent.getPointerId (aPntIter));
250 aPnt.x = theEvent.getX (aPntIter);
251 aPnt.y = theEvent.getY (aPntIter);
255 switch (myActivePointers.size())
259 PointF aPnt = myActivePointers.get (theEvent.getPointerId (0));
260 final int anX = (int )aPnt.x;
261 final int anY = (int )aPnt.y;
262 queueEvent (new Runnable() { public void run() { myRenderer.onRotation (anX, anY); }});
267 PointF aPnt1 = myActivePointers.get (myActivePointers.keyAt (0));
268 PointF aPnt2 = myActivePointers.get (myActivePointers.keyAt (1));
269 PointF aPntAver = new PointF ((aPnt1.x + aPnt2.x) * 0.5f,
270 (aPnt1.y + aPnt2.y) * 0.5f);
271 final int aDX = (int )(aPntAver.x - myPanFrom.x);
272 final int aDY = (int )(myPanFrom.y -aPntAver.y);
273 myPanFrom.x = aPntAver.x;
274 myPanFrom.y = aPntAver.y;
275 queueEvent (new Runnable() { public void run() { myRenderer.onPanning (aDX, aDY); }});
280 case MotionEvent.ACTION_UP:
281 case MotionEvent.ACTION_POINTER_UP:
282 case MotionEvent.ACTION_CANCEL:
284 myActivePointers.remove (aPointerId);
285 if (myActivePointers.size() == 0)
287 final int aPressX = (int )theEvent.getX (aPointerIndex);
288 final int aPressY = (int )theEvent.getY (aPointerIndex);
289 double aPressTimeMs = theEvent.getEventTime() - theEvent.getDownTime();
290 if (aPressTimeMs < 100.0)
292 queueEvent (new Runnable() { public void run() { myRenderer.onClick (aPressX, aPressY); }});
296 else if (myActivePointers.size() == 1)
298 PointF aPnt = myActivePointers.get (myActivePointers.keyAt (0));
299 final int aStartX = (int )aPnt.x;
300 final int aStartY = (int )aPnt.y;
301 queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
303 //queueEvent (new Runnable() { public void run() { myRenderer.onStopAction(); }});
314 queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }});
318 public void setProj (final OcctJniRenderer.TypeOfOrientation theProj)
320 queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }});
324 private OcctJniRenderer myRenderer = null;
326 //! Touch events cache
327 private SparseArray<PointF> myActivePointers = new SparseArray<PointF>();
329 //! Starting point for panning event
330 private PointF myPanFrom = new PointF (0.0f, 0.0f);