7f2debb2 |
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 | |
ceddb5ca |
41 | android.util.DisplayMetrics aDispInfo = theContext.getResources().getDisplayMetrics(); |
42 | myScreenDensity = aDispInfo.density; |
43 | |
7f2debb2 |
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 | |
ceddb5ca |
51 | myRenderer = new OcctJniRenderer (this, myScreenDensity); |
7f2debb2 |
52 | setRenderer (myRenderer); |
ceddb5ca |
53 | setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY); // render on request to spare battery |
7f2debb2 |
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); }}); |
ceddb5ca |
61 | requestRender(); |
7f2debb2 |
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 | { |
ceddb5ca |
210 | final int aMaskedAction = theEvent.getActionMasked(); |
7f2debb2 |
211 | switch (aMaskedAction) |
212 | { |
213 | case MotionEvent.ACTION_DOWN: |
214 | case MotionEvent.ACTION_POINTER_DOWN: |
215 | { |
ceddb5ca |
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) |
7f2debb2 |
221 | { |
ceddb5ca |
222 | mySelectPoint = aPnt; |
7f2debb2 |
223 | } |
ceddb5ca |
224 | else |
7f2debb2 |
225 | { |
ceddb5ca |
226 | mySelectPoint = null; |
7f2debb2 |
227 | } |
228 | |
ceddb5ca |
229 | queueEvent (new Runnable() { public void run() { myRenderer.onAddTouchPoint (aPointerId, aPnt.x, aPnt.y); }}); |
7f2debb2 |
230 | break; |
231 | } |
232 | case MotionEvent.ACTION_MOVE: |
233 | { |
234 | for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter) |
235 | { |
ceddb5ca |
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); }}); |
7f2debb2 |
239 | } |
ceddb5ca |
240 | if (mySelectPoint != null) |
7f2debb2 |
241 | { |
ceddb5ca |
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) |
7f2debb2 |
246 | { |
ceddb5ca |
247 | mySelectPoint = null; |
7f2debb2 |
248 | } |
249 | } |
250 | break; |
251 | } |
252 | case MotionEvent.ACTION_UP: |
253 | case MotionEvent.ACTION_POINTER_UP: |
254 | case MotionEvent.ACTION_CANCEL: |
255 | { |
ceddb5ca |
256 | if (mySelectPoint != null) |
7f2debb2 |
257 | { |
ceddb5ca |
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; |
7f2debb2 |
262 | } |
ceddb5ca |
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); }}); |
7f2debb2 |
268 | } |
269 | } |
ceddb5ca |
270 | requestRender(); |
7f2debb2 |
271 | return true; |
272 | } |
273 | |
274 | //! Fit All |
275 | public void fitAll() |
276 | { |
277 | queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }}); |
ceddb5ca |
278 | requestRender(); |
7f2debb2 |
279 | } |
280 | |
281 | //! Move camera |
282 | public void setProj (final OcctJniRenderer.TypeOfOrientation theProj) |
283 | { |
284 | queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }}); |
ceddb5ca |
285 | requestRender(); |
7f2debb2 |
286 | } |
287 | |
288 | //! OCCT viewer |
ceddb5ca |
289 | private OcctJniRenderer myRenderer = null; |
290 | private int mySelectId = -1; |
291 | private PointF mySelectPoint = null; |
292 | private float myScreenDensity = 1.0f; |
7f2debb2 |
293 | |
294 | } |