/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package javax.microedition.m3g;
import javax.microedition.lcdui.Graphics;
import java.util.Hashtable;
import java.util.Vector;
import com.nokia.mj.impl.rt.legacy.ToolkitInvoker;
import com.nokia.mj.impl.rt.legacy.ToolkitObserver;
public class Graphics3D
{
//------------------------------------------------------------------
// Static data
//------------------------------------------------------------------
public static final int ANTIALIAS = 2;
public static final int DITHER = 4;
public static final int TRUE_COLOR = 8;
// M3G 1.1
public static final int OVERWRITE = 16;
//------------------------------------------------------------------
// Instance data
//------------------------------------------------------------------
int handle;
private Camera camera = null;
private Vector lights = new Vector();
private java.lang.Object currentTarget = null;
private int offsetX, offsetY, hints = 0;
private boolean depthEnabled = true;
// this flag is for identification of image target types
// - True for mutable off-screen images
// - False for canvas/GameCanvas framebuffer
private boolean isImageTarget;
// this flag is for identification if MBX HW accelerator is present
// - True - MBX is NOT present
// - False - MBX is present
private boolean isProperRenderer;
// NGA specific change.
// Identifies if 2D content drawn prior M3G content to canvas
// needs to be uploaded to canvas EGL surface before
// M3G binds to EGL surface.
private boolean eglContentValid = false;
// NGA specific change.
// Class for storing and comparing
// clip and viewport rectangles
private class Rect
{
public int x;
public int y;
public int width;
public int height;
public Rect()
{
x = 0;
y = 0;
width = 0;
height = 0;
}
public Rect(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public boolean contains(Rect r)
{
return x <= r.x && y <= r.y &&
(x + width) >= (r.x + r.width) &&
(y + height) >= (r.y + r.height);
}
};
// NGA specific change.
// Viewport and clip are saved to Java side
// for checking if Background is used for clearing
// entire canvas area.
private Rect viewport;
private Rect clip;
private boolean ngaEnabled = false;
private boolean foreground;
//------------------------------------------------------------------
// Constructor(s)
//------------------------------------------------------------------
/**
*
*/
public static final Graphics3D getInstance()
{
Interface instance = Interface.getInstance();
synchronized (instance)
{
if (instance.isShutdownOngoing())
{
throw new RuntimeException("Graphics3D closed");
}
if (instance.graphics3D == null)
{
// graphics3D must be assigned to interface before asking foreground status
instance.graphics3D = new Graphics3D();
instance.graphics3D.foreground = ToolkitInvoker.getToolkitInvoker().isForeground();
}
return instance.graphics3D;
}
}
/**
*
*/
private Graphics3D()
{
ToolkitInvoker invoker = ToolkitInvoker.getToolkitInvoker();
this.handle = _ctor(Interface.getHandle());
_addRef(handle);
try
{
isProperRenderer = _isProperRenderer(Interface.bindEventSource());
}
finally
{
Interface.releaseEventSource();
}
// NGA specific change.
// Initializes the viewport and clipping rectangles
this.viewport = new Rect();
this.clip = new Rect();
// Initializes NGA status - enabled or disabled
ngaEnabled = invoker.isNgaEnabled();
}
/**
*
*/
void doDestroyNotify()
{
this.camera = null;
this.lights = null;
}
//------------------------------------------------------------------
// Public methods
//------------------------------------------------------------------
/**
*/
public void bindTarget(java.lang.Object target)
{
bindTarget(target, true, 0);
}
/**
*
*/
public void bindTarget(java.lang.Object target, boolean depth, int flags)
{
synchronized (Interface.getInstance())
{
integrityCheck();
int eventSrcHandle = 0;
if (currentTarget != null)
{
throw new IllegalStateException();
}
if (target == null)
{
throw new NullPointerException();
}
try
{
// Bind event source. This need to be released.
eventSrcHandle = Interface.bindEventSource();
if (target instanceof Graphics)
{
Graphics g = (Graphics) target;
Platform.sync(g);
if (g.getClipWidth() > Defs.MAX_VIEWPORT_WIDTH ||
g.getClipHeight() > Defs.MAX_VIEWPORT_HEIGHT)
{
throw new IllegalArgumentException();
}
offsetX = g.getTranslateX();
offsetY = g.getTranslateY();
ToolkitInvoker invoker = ToolkitInvoker.getToolkitInvoker();
// NGA specific change.
if (ngaEnabled)
{
// If overwrite is set, there is no need
// to update EGL surface with 2D content
eglContentValid = (flags & OVERWRITE) > 0;
// Clip and viewport are stored for later
// checks regarding Background clear
clip.x = g.getClipX() + offsetX;
clip.y = g.getClipY() + offsetY;
clip.width = g.getClipWidth();
clip.height = g.getClipHeight();
viewport.x = clip.x;
viewport.y = clip.y;
viewport.width = clip.width;
viewport.height = clip.height;
isImageTarget = _bindGraphics(
eventSrcHandle,
handle,
invoker.graphicsGetHandle(g),
clip.x, clip.y,
clip.width, clip.height,
depth, flags,
isProperRenderer);
}
else
{
isImageTarget = _bindGraphics(
eventSrcHandle,
handle,
invoker.graphicsGetHandle(g),
g.getClipX() + offsetX, g.getClipY() + offsetY,
g.getClipWidth(), g.getClipHeight(),
depth, flags,
isProperRenderer);
}
currentTarget = g;
}
else if (target instanceof Image2D)
{
Image2D img = (Image2D) target;
offsetX = offsetY = 0;
_bindImage(eventSrcHandle, handle, img.handle, depth, flags);
currentTarget = img;
}
else
{
throw new IllegalArgumentException();
}
}
finally
{
// Release event source
Interface.releaseEventSource();
}
hints = flags;
depthEnabled = depth;
}
}
/**
*
*/
public void releaseTarget()
{
synchronized (Interface.getInstance())
{
integrityCheck();
if (currentTarget == null)
{
return;
}
int eventSrcHandle = 0;
try
{
// Bind event source
eventSrcHandle = Interface.bindEventSource();
if (currentTarget instanceof Graphics)
{
Graphics g = (Graphics) currentTarget;
ToolkitInvoker invoker = ToolkitInvoker.getToolkitInvoker();
// NGA specific change.
if (ngaEnabled)
{
updateEglContent();
}
_releaseGraphics(eventSrcHandle, handle,
invoker.graphicsGetHandle(g), isImageTarget, isProperRenderer);
}
else if (currentTarget instanceof Image2D)
{
_releaseImage(eventSrcHandle, handle);
}
else
{
throw new Error();
}
}
finally
{
currentTarget = null;
if (ngaEnabled && !foreground && eventSrcHandle != 0)
{
_freeGLESResources(eventSrcHandle, handle);
}
// Release event source
Interface.releaseEventSource();
}
}
}
/**
*
*/
public void setViewport(int x, int y, int width, int height)
{
integrityCheck();
if (width <= 0 || height <= 0
|| width > Defs.MAX_VIEWPORT_DIMENSION
|| height > Defs.MAX_VIEWPORT_DIMENSION)
{
throw new IllegalArgumentException();
}
// NGA specific change.
if (ngaEnabled)
{
// Update the viewport info.
viewport.x = x + offsetX;
viewport.y = y + offsetY;
viewport.width = width;
viewport.height = height;
_setViewport(handle, viewport.x, viewport.y, viewport.width, viewport.height);
}
else
{
_setViewport(handle, x + offsetX, y + offsetY, width, height);
}
}
/**
*
*/
public void clear(Background background)
{
integrityCheck();
if (getTarget() == null)
{
throw new java.lang.IllegalStateException(
"Graphics3D does not have a rendering target");
}
// NGA specific change.
if (ngaEnabled)
{
checkBackgroundCleared(background);
}
try
{
_clear(Interface.bindEventSource(),
handle, background != null ? background.handle : 0);
}
finally
{
Interface.releaseEventSource();
}
}
/**
*
*/
public void render(World world)
{
integrityCheck();
// NGA specific change.
if (ngaEnabled)
{
checkBackgroundCleared(world.getBackground());
}
try
{
_renderWorld(Interface.bindEventSource(), handle, world.handle);
}
finally
{
Interface.releaseEventSource();
}
}
/**
*
*/
public void render(VertexBuffer vertices,
IndexBuffer primitives,
Appearance appearance,
Transform transform)
{
// Call rendering method with default visibility
render(vertices, primitives, appearance, transform, -1);
}
/**
*
*/
public void render(VertexBuffer vertices,
IndexBuffer primitives,
Appearance appearance,
Transform transform,
int scope)
{
// null pointer exceptions thrown automatically below
integrityCheck();
// NGA specific change.
if (ngaEnabled)
{
updateEglContent();
}
try
{
_render(Interface.bindEventSource(),
handle,
vertices.handle,
primitives.handle,
appearance.handle,
transform != null ? transform.matrix : null,
scope);
}
finally
{
Interface.releaseEventSource();
}
}
/**
*
*/
public void render(Node node, Transform transform)
{
if (!(node instanceof Mesh
|| node instanceof Sprite3D
|| node instanceof Group)
&& node != null)
{
throw new IllegalArgumentException();
}
integrityCheck();
// NGA specific change.
if (ngaEnabled)
{
updateEglContent();
}
try
{
_renderNode(Interface.bindEventSource(),
handle,
node.handle,
transform != null ? transform.matrix : null);
}
finally
{
Interface.releaseEventSource();
}
}
public void setCamera(Camera camera, Transform transform)
{
integrityCheck();
_setCamera(handle,
camera != null ? camera.handle : 0,
transform != null ? transform.matrix : null);
this.camera = camera;
}
/**
*/
public int addLight(Light light, Transform transform)
{
integrityCheck();
int index = _addLight(handle,
light.handle,
transform != null ? transform.matrix : null);
if (lights.size() < index + 1)
{
lights.setSize(index + 1);
}
lights.setElementAt(light, index);
return index;
}
/**
*
*/
public void setLight(int index, Light light, Transform transform)
{
integrityCheck();
_setLight(handle,
index,
light != null ? light.handle : 0,
transform != null ? transform.matrix : null);
lights.setElementAt(light, index);
}
/**
*/
public void resetLights()
{
integrityCheck();
_resetLights(handle);
lights.removeAllElements();
}
/**
*
*/
public static final Hashtable getProperties()
{
Hashtable props = new Hashtable();
props.put("supportAntialiasing", new java.lang.Boolean(
_isAASupported(Interface.getHandle())));
props.put("supportTrueColor", new java.lang.Boolean(Defs.supportTrueColor));
props.put("supportDithering", new java.lang.Boolean(Defs.supportDithering));
props.put("supportMipmapping", new java.lang.Boolean(Defs.supportMipmapping));
props.put("supportPerspectiveCorrection", new java.lang.Boolean(Defs.supportPerspectiveCorrection));
props.put("supportLocalCameraLighting", new java.lang.Boolean(Defs.supportLocalCameraLighting));
props.put("maxLights", new java.lang.Integer(Defs.MAX_LIGHTS));
props.put("maxViewportWidth", new java.lang.Integer(Defs.MAX_VIEWPORT_WIDTH));
props.put("maxViewportHeight", new java.lang.Integer(Defs.MAX_VIEWPORT_HEIGHT));
props.put("maxViewportDimension", new java.lang.Integer(Defs.MAX_VIEWPORT_DIMENSION));
props.put("maxTextureDimension", new java.lang.Integer(Defs.MAX_TEXTURE_DIMENSION));
props.put("maxSpriteCropDimension", new java.lang.Integer(Defs.MAX_TEXTURE_DIMENSION));
props.put("numTextureUnits", new java.lang.Integer(Defs.NUM_TEXTURE_UNITS));
props.put("maxTransformsPerVertex", new java.lang.Integer(Defs.MAX_TRANSFORMS_PER_VERTEX));
// Extra properties
props.put("m3gRelease", new java.lang.String("04_wk49"));
return props;
}
/**
*
*/
public void setDepthRange(float near, float far)
{
integrityCheck();
_setDepthRange(handle, near, far);
}
// M3G 1.1
public Camera getCamera(Transform transform)
{
integrityCheck();
if (transform != null)
{
_getViewTransform(handle, transform.matrix);
}
return (Camera) Object3D.getInstance(_getCamera(handle));
}
public float getDepthRangeFar()
{
integrityCheck();
return _getDepthRangeFar(handle);
}
public float getDepthRangeNear()
{
integrityCheck();
return _getDepthRangeNear(handle);
}
public Light getLight(int index, Transform transform)
{
integrityCheck();
if (index < 0 || index >= _getLightCount(handle))
{
throw new IndexOutOfBoundsException();
}
return (Light) Object3D.getInstance(_getLightTransform(handle,
index,
transform != null ? transform.matrix : null));
}
public int getLightCount()
{
integrityCheck();
return _getLightCount(handle);
}
public java.lang.Object getTarget()
{
return currentTarget;
}
public int getViewportHeight()
{
integrityCheck();
return _getViewportHeight(handle);
}
public int getViewportWidth()
{
integrityCheck();
return _getViewportWidth(handle);
}
public int getViewportX()
{
integrityCheck();
return _getViewportX(handle) - offsetX;
}
public int getViewportY()
{
integrityCheck();
return _getViewportY(handle) - offsetY;
}
public int getHints()
{
return hints;
}
public boolean isDepthBufferEnabled()
{
return depthEnabled;
}
// M3G 1.1 getters END
//------------------------------------------------------------------
// Native implementation methods
//------------------------------------------------------------------
// Finalization method for Symbian
final private void registeredFinalize()
{
// NOP -- handled via ToolkitObserver
}
private void integrityCheck()
{
if (Interface.isShutdownOngoing())
{
throw new RuntimeException("Graphics3D closed");
}
}
/**
* NGA specific change.
* Checks if Background is used for clearing the full graphics target area.
* If so, the 2D content prior to M3G content does not need to be
* uploaded to EGL surface.
*/
private void checkBackgroundCleared(Background bg)
{
if (!isCanvasTarget())
{
return;
}
if (bg != null && bg.isColorClearEnabled() && !eglContentValid)
{
Graphics g = (Graphics)currentTarget;
int graphicsHandle = ToolkitInvoker.getToolkitInvoker().graphicsGetHandle(g);
int targetWidth = _getTargetWidth(graphicsHandle);
int targetHeight = _getTargetHeight(graphicsHandle);
Rect targetRect = new Rect(0, 0, targetWidth, targetHeight);
// Checks that target is fully covered by graphics clip and viewport
eglContentValid = viewport.contains(targetRect) && clip.contains(targetRect);
}
if (!eglContentValid)
{
updateEglContent();
}
}
/**
* NGA specific change.
* Checks if the rendering target is a Graphics instance obtained from LCDUI Canvas.
* @return true if the target is Canvas Graphics.
*/
private boolean isCanvasTarget()
{
return (currentTarget != null) &&
(currentTarget instanceof Graphics) &&
!isImageTarget;
}
/**
* NGA specific change.
* Notifies the native side that egl content need to be updated
* thanks to 2d mixture
*/
private void updateEglContent()
{
if (isCanvasTarget() && !eglContentValid)
{
Graphics g = (Graphics)currentTarget;
int graphicsHandle = ToolkitInvoker.getToolkitInvoker().graphicsGetHandle(g);
try
{
_updateEglContent(Interface.bindEventSource(), graphicsHandle);
eglContentValid = true;
}
finally
{
Interface.releaseEventSource();
}
}
}
void setForeground(boolean foreground)
{
synchronized (Interface.getInstance())
{
this.foreground = foreground;
if (ngaEnabled &&
!foreground &&
currentTarget == null)
{
try
{
_freeGLESResources(Interface.bindEventSource(), handle);
}
finally
{
Interface.releaseEventSource();
}
}
}
}
private native static int _ctor(int hInterface);
private native static void _addRef(int hObject);
private native static int _addLight(int handle,
int hLight,
byte[] transform);
private native static boolean _bindGraphics(int eventSourceHandle,
int handle,
int graphicsHandle,
int clipX, int clipY,
int clipW, int clipH,
boolean depth,
int hintBits,
boolean aIsProperRenderer);
private native static void _bindImage(int eventSourceHandle, int handle, int imgHandle, boolean depth, int hintBits);
private native static void _releaseGraphics(int eventSourceHandle,
int handle,
int graphicsHandle,
boolean aIsImageTarget,
boolean aIsProperRenderer);
private native static void _releaseImage(int eventSourceHandle, int handle);
private native static void _resetLights(int handle);
private native static void _clear(int eventSourceHandle, int handle, int hBackground);
private native static void _render(int eventSourceHandle,
int handle,
int hVtxBuffer,
int hIdxBuffer,
int hAppearance,
byte[] transform,
int scope);
private native static void _renderNode(int eventSourceHandle, int handle, int hNode, byte[] transform);
private native static void _renderWorld(int eventSourceHandle, int handle, int hWorld);
private native static void _setCamera(int handle,
int hCamera,
byte[] transform);
private native static void _setViewport(int handle,
int x, int y,
int width, int height);
private native static void _setLight(int handle,
int index,
int hLight,
byte[] transform);
private native static void _setDepthRange(int handle,
float near,
float far);
// M3G 1.1
// Maintenance release getters
private native static void _getViewTransform(int handle,
byte[] transform);
private native static int _getCamera(int handle);
private native static int _getLightTransform(int handle,
int index,
byte[] transform);
private native static int _getLightCount(int handle);
private native static float _getDepthRangeNear(int handle);
private native static float _getDepthRangeFar(int handle);
private native static int _getViewportX(int handle);
private native static int _getViewportY(int handle);
private native static int _getViewportWidth(int handle);
private native static int _getViewportHeight(int handle);
/* Statistics support, MUST be disabled in official releases! */
/*
public native static int getStatistics(int[] statistics);
*/
private native static boolean _isAASupported(int handle);
private native static boolean _isProperRenderer(int handle);
// NGA specific changes.
private native static int _getTargetHeight(int graphicsHandle);
private native static int _getTargetWidth(int graphicsHandle);
private native static void _updateEglContent(int eventSourceHandle,
int graphicsHandle);
private native static void _freeGLESResources(int eventSourceHandle,
int handle);
}