|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 package javax.microedition.m3g; |
|
19 |
|
20 import java.lang.ref.WeakReference; |
|
21 import java.util.Hashtable; |
|
22 import org.eclipse.swt.widgets.Display; |
|
23 #ifdef RD_JAVA_OMJ |
|
24 import com.nokia.mj.impl.rt.support.Finalizer; |
|
25 #endif // RD_JAVA_OMJ |
|
26 |
|
27 /** |
|
28 * M3G interface object. An interface is automatically created for |
|
29 * each MIDlet using the 3D API to keep track of Java-side object |
|
30 * lifetimes etc. |
|
31 */ |
|
32 class Interface |
|
33 { |
|
34 //------------------------------------------------------------------ |
|
35 // Static data |
|
36 //------------------------------------------------------------------ |
|
37 |
|
38 // Common class enumeration for Java and native code |
|
39 |
|
40 private static final int ANIMATION_CONTROLLER = 0x01; |
|
41 private static final int ANIMATION_TRACK = 0x02; |
|
42 private static final int APPEARANCE = 0x03; |
|
43 private static final int BACKGROUND = 0x04; |
|
44 private static final int CAMERA = 0x05; |
|
45 private static final int COMPOSITING_MODE = 0x06; |
|
46 private static final int FOG = 0x07; |
|
47 private static final int GROUP = 0x08; |
|
48 private static final int IMAGE_2D = 0x09; |
|
49 private static final int INDEX_BUFFER = 0x0A; |
|
50 private static final int KEYFRAME_SEQUENCE = 0x0B; |
|
51 private static final int LIGHT = 0x0C; |
|
52 private static final int LOADER = 0x0D; |
|
53 private static final int MATERIAL = 0x0E; |
|
54 private static final int MESH = 0x0F; |
|
55 private static final int MORPHING_MESH = 0x10; |
|
56 private static final int POLYGON_MODE = 0x11; |
|
57 private static final int RENDER_CONTEXT = 0x12; |
|
58 private static final int SKINNED_MESH = 0x13; |
|
59 private static final int SPRITE_3D = 0x14; |
|
60 private static final int TEXTURE_2D = 0x15; |
|
61 private static final int VERTEX_ARRAY = 0x16; |
|
62 private static final int VERTEX_BUFFER = 0x17; |
|
63 private static final int WORLD = 0x18; |
|
64 |
|
65 // Once created, the interface singleton currently remains in |
|
66 // memory until VM exit. By using a WeakReference here, with hard |
|
67 // references stored in each object, it could be GC'd when no more |
|
68 // objects exist, but that probably isn't worth the extra memory |
|
69 // overhead. |
|
70 |
|
71 //private static Hashtable s_instances = new Hashtable(); |
|
72 private static Interface instance = null; |
|
73 |
|
74 //------------------------------------------------------------------ |
|
75 // Instance data |
|
76 //------------------------------------------------------------------ |
|
77 |
|
78 /** |
|
79 * Handle of the native interface object. |
|
80 */ |
|
81 private int handle; |
|
82 |
|
83 /** |
|
84 * Global handle-to-Object3D map used to both find the Java |
|
85 * counterparts of objects returned from the native methods, and |
|
86 * keep certain objects from being garbage collected. |
|
87 */ |
|
88 private final Hashtable liveObjects = new Hashtable(); |
|
89 |
|
90 /** |
|
91 * Flag for shutdown signal |
|
92 */ |
|
93 private boolean iShutdown = false; |
|
94 |
|
95 /** |
|
96 * Flag for native peer init state |
|
97 */ |
|
98 private boolean iNativeInitialized = false; |
|
99 |
|
100 |
|
101 #ifdef RD_JAVA_OMJ |
|
102 private Finalizer mFinalizer; |
|
103 #endif // RD_JAVA_OMJ |
|
104 |
|
105 //------------------------------------------------------------------ |
|
106 // Constructors |
|
107 //------------------------------------------------------------------ |
|
108 |
|
109 private Interface() |
|
110 { |
|
111 |
|
112 // Contruct native peer |
|
113 initNativePeer(); |
|
114 |
|
115 #ifdef RD_JAVA_OMJ |
|
116 mFinalizer = new Finalizer() |
|
117 { |
|
118 public void finalizeImpl() |
|
119 { |
|
120 doFinalize(); |
|
121 } |
|
122 }; |
|
123 #else // RD_JAVA_OMJ |
|
124 Platform.registerFinalizer(this); |
|
125 #endif // RD_JAVA_OMJ |
|
126 } |
|
127 |
|
128 //------------------------------------------------------------------ |
|
129 // Package methods |
|
130 //------------------------------------------------------------------ |
|
131 |
|
132 /** |
|
133 * Returns the M3G interface instance for the current MIDlet. |
|
134 */ |
|
135 static final Interface getInstance() |
|
136 { |
|
137 if (instance == null) |
|
138 { |
|
139 instance = new Interface(); |
|
140 } |
|
141 return instance; |
|
142 } |
|
143 |
|
144 /** |
|
145 * Returns the native handle of the current Interface instance. |
|
146 */ |
|
147 static final int getHandle() |
|
148 { |
|
149 getInstance().integrityCheck(); |
|
150 return getInstance().handle; |
|
151 } |
|
152 |
|
153 /** |
|
154 * Registers an Object3D with this interface. The object is added |
|
155 * to the global handle-to-object map, and the native finalization |
|
156 * callback is set up. The handle of the object must already be |
|
157 * set at this point! |
|
158 */ |
|
159 static final void register(Object3D obj) |
|
160 { |
|
161 Platform.registerFinalizer(obj); |
|
162 getInstance().liveObjects.put(new Integer(obj.handle), |
|
163 new WeakReference(obj)); |
|
164 } |
|
165 |
|
166 static final void register(Loader obj) |
|
167 { |
|
168 Platform.registerFinalizer(obj); |
|
169 getInstance().liveObjects.put(new Integer(obj.handle), |
|
170 new WeakReference(obj)); |
|
171 } |
|
172 |
|
173 /** |
|
174 * Finds an Object3D in the global handle-to-object map. Also |
|
175 * removes dead objects (that is, null references) from the map |
|
176 * upon encountering them. |
|
177 */ |
|
178 static final Object3D findObject(int handle) |
|
179 { |
|
180 Interface self = getInstance(); |
|
181 Integer iHandle = new Integer(handle); |
|
182 Object ref = self.liveObjects.get(iHandle); |
|
183 |
|
184 if (ref != null) |
|
185 { |
|
186 Object3D obj = (Object3D)((WeakReference)ref).get(); |
|
187 if (obj == null) |
|
188 { |
|
189 self.liveObjects.remove(iHandle); |
|
190 } |
|
191 return obj; |
|
192 } |
|
193 else |
|
194 { |
|
195 return null; |
|
196 } |
|
197 } |
|
198 |
|
199 /** |
|
200 * Returns the Java object representing a native object, or |
|
201 * creates a new proxy/peer if one doesn't exist yet. |
|
202 */ |
|
203 static final Object3D getObjectInstance(int handle) |
|
204 { |
|
205 |
|
206 // A zero handle equals null |
|
207 |
|
208 if (handle == 0) |
|
209 { |
|
210 return null; |
|
211 } |
|
212 |
|
213 // Then try to find an existing Java representative for the |
|
214 // object |
|
215 |
|
216 Object3D obj = findObject(handle); |
|
217 if (obj != null) |
|
218 { |
|
219 return obj; |
|
220 } |
|
221 |
|
222 // Not found, create a new Java object. Note that only |
|
223 // non-abstract classes can possibly be returned. |
|
224 |
|
225 switch (_getClassID(handle)) |
|
226 { |
|
227 case ANIMATION_CONTROLLER: |
|
228 return new AnimationController(handle); |
|
229 case ANIMATION_TRACK: |
|
230 return new AnimationTrack(handle); |
|
231 case APPEARANCE: |
|
232 return new Appearance(handle); |
|
233 case BACKGROUND: |
|
234 return new Background(handle); |
|
235 case CAMERA: |
|
236 return new Camera(handle); |
|
237 case COMPOSITING_MODE: |
|
238 return new CompositingMode(handle); |
|
239 case FOG: |
|
240 return new Fog(handle); |
|
241 case GROUP: |
|
242 return new Group(handle); |
|
243 case IMAGE_2D: |
|
244 return new Image2D(handle); |
|
245 case INDEX_BUFFER: |
|
246 return new TriangleStripArray(handle); |
|
247 case KEYFRAME_SEQUENCE: |
|
248 return new KeyframeSequence(handle); |
|
249 case LIGHT: |
|
250 return new Light(handle); |
|
251 //case LOADER: |
|
252 case MATERIAL: |
|
253 return new Material(handle); |
|
254 case MESH: |
|
255 return new Mesh(handle); |
|
256 case MORPHING_MESH: |
|
257 return new MorphingMesh(handle); |
|
258 case POLYGON_MODE: |
|
259 return new PolygonMode(handle); |
|
260 //case RENDER_CONTEXT: |
|
261 case SKINNED_MESH: |
|
262 return new SkinnedMesh(handle); |
|
263 case SPRITE_3D: |
|
264 return new Sprite3D(handle); |
|
265 case TEXTURE_2D: |
|
266 return new Texture2D(handle); |
|
267 case VERTEX_ARRAY: |
|
268 return new VertexArray(handle); |
|
269 case VERTEX_BUFFER: |
|
270 return new VertexBuffer(handle); |
|
271 case WORLD: |
|
272 return new World(handle); |
|
273 default: |
|
274 throw new Error(); |
|
275 } |
|
276 } |
|
277 |
|
278 /** |
|
279 * Forces removal of an object from the handle-to-object map. |
|
280 */ |
|
281 static final void deregister(Object3D obj, Interface self) |
|
282 { |
|
283 self.liveObjects.remove(new Integer(obj.handle)); |
|
284 if (self.liveObjects.isEmpty() && self.iShutdown) |
|
285 { |
|
286 self.registeredFinalize(); |
|
287 } |
|
288 } |
|
289 |
|
290 /** |
|
291 * Forces removal of an object from the handle-to-object map. |
|
292 */ |
|
293 static final void deregister(Loader obj, Interface self) |
|
294 { |
|
295 self.liveObjects.remove(new Integer(obj.handle)); |
|
296 if (self.liveObjects.isEmpty() && self.iShutdown) |
|
297 { |
|
298 self.registeredFinalize(); |
|
299 } |
|
300 } |
|
301 |
|
302 /** |
|
303 * Sets shutdown indication flag. Actual native |
|
304 * cleanup occurs when liveObjects count is zero |
|
305 */ |
|
306 void signalShutdown() |
|
307 { |
|
308 iShutdown = true; |
|
309 } |
|
310 |
|
311 /** |
|
312 * Gets the state of this interface |
|
313 * @return true if interface is fully constructed, otherwise false |
|
314 */ |
|
315 boolean isFullyInitialized() |
|
316 { |
|
317 return iNativeInitialized; |
|
318 } |
|
319 |
|
320 //------------------------------------------------------------------ |
|
321 // Private methods |
|
322 //------------------------------------------------------------------ |
|
323 |
|
324 /** |
|
325 * Checks the status of the native interface |
|
326 */ |
|
327 private void integrityCheck() |
|
328 { |
|
329 if (!iNativeInitialized) |
|
330 { |
|
331 // If native interface cannot be initialized we cannot recover from it |
|
332 if (!initNativePeer()) |
|
333 { |
|
334 throw new Error("UI thread not available"); |
|
335 } |
|
336 } |
|
337 } |
|
338 |
|
339 /** |
|
340 * Initializes native peer |
|
341 * @return true if native interface was succesfully inialized otherwise false |
|
342 */ |
|
343 private boolean initNativePeer() |
|
344 { |
|
345 if (iNativeInitialized) |
|
346 { |
|
347 return true; |
|
348 } |
|
349 if (Platform.uiThreadAvailable()) |
|
350 { |
|
351 Platform.executeInUIThread( |
|
352 new M3gRunnable() |
|
353 { |
|
354 public void doRun() |
|
355 { |
|
356 handle = _ctor(); |
|
357 } |
|
358 }); |
|
359 iNativeInitialized = true; |
|
360 return true; |
|
361 } |
|
362 else |
|
363 { |
|
364 return false; |
|
365 } |
|
366 } |
|
367 |
|
368 |
|
369 #ifdef RD_JAVA_OMJ |
|
370 private void doFinalize() |
|
371 { |
|
372 if (mFinalizer != null) |
|
373 { |
|
374 registeredFinalize(); |
|
375 mFinalizer = null; |
|
376 } |
|
377 } |
|
378 #endif // RD_JAVA_OMJ |
|
379 |
|
380 // Native finalization hook, for Symbian only |
|
381 final private void registeredFinalize() |
|
382 { |
|
383 if (Interface.instance != null) |
|
384 { |
|
385 // Finalize M3G interface |
|
386 Platform.executeInUIThread( |
|
387 new M3gRunnable() |
|
388 { |
|
389 public void doRun() |
|
390 { |
|
391 Platform.finalizeInterface(handle); |
|
392 } |
|
393 }); |
|
394 Interface.instance = null; |
|
395 } |
|
396 } |
|
397 |
|
398 // Native constructor |
|
399 private static native int _ctor(); |
|
400 |
|
401 // Native class ID resolver |
|
402 private static native int _getClassID(int hObject); |
|
403 } |