/*
 * $RCSfile$
 *
 * Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision$
 * $Date$
 * $State$
 */

#include "Stdafx.h"

D3dCtxVector d3dCtxList;

/*
 * Use the following code to initialize ctx property :
 *
 * D3dCtx ctx* = new D3dCtx(env, obj, hwnd, offScreen, vid);
 * if (ctx->initialize(env, obj)) {
 *     delete ctx;
 * }
 * d3dCtxList.push_back(ctx);
 *
 *
 * When ctx remove :
 *
 * d3dCtxList.erase(find(d3dCtxList.begin(), d3dCtxList.end(), ctx);
 * delete ctx;
 *
 */

const D3DXMATRIX identityMatrix = D3DXMATRIX(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);

D3dCtx::D3dCtx(JNIEnv* env, jobject obj, HWND _hwnd, BOOL _offScreen,
	       jint vid)
{
    int i;
    jniEnv = env;
    monitor = NULL;
    hwnd = _hwnd;
    pD3D = NULL;
    pDevice = NULL;
    offScreen = _offScreen;
    offScreenWidth = 0;
    offScreenHeight = 0;

    driverInfo = NULL;
    deviceInfo = NULL;

    depthStencilSurface = NULL;
    frontSurface = NULL;
    backSurface = NULL;
    resetColorTarget = false;
    forceResize = false;
    inToggle = false;
    useFreeList0 = true;
    reIndexifyTable = NULL;
	bFastDrawQuads = getSystemProperty(env,"j3d.d3dForceFastQuads","true");

    // set default RenderingState variable
    cullMode = D3DCULL_CW;
    fillMode = D3DFILL_SOLID;
    zWriteEnable = TRUE;
    zEnable = TRUE;

    // this is the pixelFormat return from NativeConfigTemplate.
    minZDepth = vid & 0x3fffffff;
    if (vid & 0x80000000) 
	{
	  antialiasing = REQUIRED;
    } 
	else 
	 if (vid & 0x40000000) 
	  {
	   antialiasing = PREFERRED;
      } 
	 else 
	 {
	  antialiasing = UNNECESSARY;
     }

    setFullScreenFromProperty(env);

    if (offScreen) {
	// disable fullscreen mode for offscreen
	bFullScreen = false;
	bFullScreenRequired = false;
    }

    dlTableSize = DISPLAYLIST_INITSIZE;
    displayListTable = new LPD3DDISPLAYLIST[dlTableSize];
    if (displayListTable == NULL) {
	error(OUTOFMEMORY);
	exit(1);
    }
    for (i=0; i < dlTableSize; i++) {
	displayListTable[i] = NULL;
    }

    currDisplayListID = 0;
    quadIndexBuffer = NULL;
    quadIndexBufferSize = 0;
    lineModeIndexBuffer = NULL;

    srcVertexBuffer = NULL;
    dstVertexBuffer = NULL;


    multiTextureSupport = false;
    texUnitStage = 0;
    twoSideLightingEnable = false;
    bindTextureId = NULL;
    bindTextureIdLen = 0;

    textureTable = (LPDIRECT3DTEXTURE9 *) malloc(
  	  	       sizeof(LPDIRECT3DTEXTURE9) * TEXTURETABLESIZE);

    if (textureTable == NULL) {
	error(OUTOFMEMORY);
	exit(1);
    }
    ZeroMemory(textureTable, sizeof(LPDIRECT3DTEXTURE9)*TEXTURETABLESIZE);
    textureTableLen = TEXTURETABLESIZE;

    bindTextureId = NULL;

    volumeTable = (LPDIRECT3DVOLUMETEXTURE9 *) malloc(
  	  	       sizeof(LPDIRECT3DVOLUMETEXTURE9) * TEXTURETABLESIZE);

    if (volumeTable == NULL) {
	error(OUTOFMEMORY);
	exit(1);
    }
    ZeroMemory(volumeTable, sizeof(LPDIRECT3DVOLUMETEXTURE9)*TEXTURETABLESIZE);
    volumeTableLen = TEXTURETABLESIZE;


    cubeMapTable = (LPDIRECT3DCUBETEXTURE9 *) malloc(
  	  	       sizeof(LPDIRECT3DCUBETEXTURE9) * TEXTURETABLESIZE);

    if (cubeMapTable == NULL) {
	error(OUTOFMEMORY);
	exit(1);
    }
    ZeroMemory(cubeMapTable, sizeof(LPDIRECT3DCUBETEXTURE9)*TEXTURETABLESIZE);
    cubeMapTableLen = TEXTURETABLESIZE;


    if (hwnd == 0) {
	// Offscreen rendering
	hwnd = GetDesktopWindow();
	topHwnd = hwnd;
    } else {
	topHwnd = getTopWindow(hwnd);
    }

    if (d3dDriverList == NULL) {

	// keep trying to initialize even though
	// last time it fail.
        D3dDriverInfo::initialize(env);
    }

    if (d3dDriverList == NULL) {
	/*
	 * This happen when either
	 * (1) D3D v9.0 not install or
	 * (2) Not enough memory    or
	 * (3) No adapter found in the system.
	 */
	SafeRelease(pD3D);
        return;
    }

    pD3D = Direct3DCreate9( D3D_SDK_VERSION );

    if (pD3D == NULL) {
        error(D3DNOTFOUND);
	return;
    }
    // find current monitor handle before
    // get current display mode
    monitor = findMonitor();

    // check current display mode
    enumDisplayMode(&devmode);

    if (devmode.dmBitsPerPel < 16) {
	// tell user switch to at least 16 bit color next time
	warning(NEEDSWITCHMODE);
    }

    // find the adapter for this
    setDriverInfo();

    GetWindowRect(topHwnd, &savedTopRect);
    winStyle = GetWindowLong(topHwnd, GWL_STYLE);

    for (i=0; i < 4; i++) {
	rasterRect[i].sx = 0;
	rasterRect[i].sy = 0;
	rasterRect[i].sz = 0;
	rasterRect[i].rhw = 0;
    }

    rasterRect[0].tu = 0;
    rasterRect[0].tv = 1;
    rasterRect[1].tu = 0;
    rasterRect[1].tv = 0;
    rasterRect[2].tu = 1;
    rasterRect[2].tv = 1;
    rasterRect[3].tu = 1;
    rasterRect[3].tv = 0;

    // initialize Ambient Material
    ambientMaterial.Power = 0;
    CopyColor(ambientMaterial.Emissive, 0, 0, 0, 1.0f);
    CopyColor(ambientMaterial.Diffuse,  0, 0, 0, 1.0f);
    CopyColor(ambientMaterial.Ambient,  1.0f, 1.0f, 1.0f, 1.0f);
    CopyColor(ambientMaterial.Specular, 0, 0, 0, 1.0f);
    GetWindowRect(hwnd, &windowRect);
}

D3dCtx::~D3dCtx()
{
    release();
    SafeRelease(pD3D);
}

VOID D3dCtx::releaseTexture()
{

    for (int i=0; i < bindTextureIdLen; i++) {
	if (bindTextureId[i] > 0) {
	    pDevice->SetTexture(i, NULL);
	}
    }

    lockSurfaceList();
    if (textureTable != NULL) {
	// free all textures
	for (int i=0; i < textureTableLen; i++) {
	    SafeRelease(textureTable[i]);
	}
	SafeFree(textureTable);
    }

    if (volumeTable != NULL) {
	for (int i=0; i < volumeTableLen; i++) {
	    SafeRelease(volumeTable[i]);
	}
	SafeFree(volumeTable);

    }


    if (cubeMapTable != NULL) {
	for (int i=0; i < cubeMapTableLen; i++) {
	    SafeRelease(cubeMapTable[i]);
	}
	SafeFree(cubeMapTable);

    }

    textureTableLen = 0;
    volumeTableLen = 0;
    cubeMapTableLen = 0;
    unlockSurfaceList();

    lockImage();
    D3dImageComponent::remove(&RasterList, this);
    unlockImage();

    lockBackground();
    D3dImageComponent::remove(&BackgroundImageList, this);
    unlockBackground();

    // free list0
    freeList();
    // free list1
    freeList();
}

VOID D3dCtx::setViewport()
{
    int renderWidth = getWidth();
    int renderHeight = getHeight();
    HRESULT hr;
    D3DVIEWPORT9 vp = {0, 0, renderWidth, renderHeight, 0.0f, 1.0f};

    hr = pDevice->SetViewport( &vp );

    if (FAILED(hr)) {
	// Use the previous Viewport if fail
	error(VIEWPORTFAIL, hr);
    }
}

VOID D3dCtx::releaseVB()
{

    if (displayListTable != NULL) {
	// entry 0 is not used
	for (int i=1; i < dlTableSize; i++) {
	    SafeDelete(displayListTable[i]);
	}
	SafeFree(displayListTable);
	dlTableSize = 0;
    }


    lockGeometry();

    D3dVertexBuffer *p  = vertexBufferTable.next;
    D3dVertexBuffer *q, **r;
    D3dVertexBufferVector *vbVector;
    boolean found = false;

    while (p != NULL) {
	vbVector = p->vbVector;
	if (vbVector != NULL) {
	   for (ITER_LPD3DVERTEXBUFFER r = vbVector->begin();
			  r != vbVector->end(); ++r) {
		if (*r == p) {
		    vbVector->erase(r);
		    found = true;
		    break;
		}
	    }
	}
	q = p;
	p = p->next;
	delete q;
    }

    vertexBufferTable.next = NULL;

    freeVBList0.clear();
    freeVBList1.clear();

    unlockGeometry();

}

VOID D3dCtx::release()
{
    D3dImageComponent::removeAll(&BackgroundImageList);
    D3dImageComponent::removeAll(&RasterList);
    releaseTexture();
    SafeFree(bindTextureId);
    bindTextureIdLen = 0;


    SafeRelease(srcVertexBuffer);
    SafeRelease(dstVertexBuffer);
    SafeRelease(quadIndexBuffer);
    SafeRelease(lineModeIndexBuffer);
    quadIndexBufferSize = 0;
    releaseVB();

    // trying to free VertexBuffer
    // This will crash the driver if Indices/StreamSource
    // Not set before.
    //	pDevice->SetIndices(NULL, 0);
    // pDevice->SetStreamSource(0, NULL, 0);
    SafeRelease(depthStencilSurface);
    SafeRelease(frontSurface);
    SafeRelease(backSurface);

    SafeRelease(pDevice);
    currDisplayListID = 0;
    multiTextureSupport = false;
    texUnitStage = 0;
    twoSideLightingEnable = false;
    freePointerList();
    freePointerList();
}



/*
 * Application should delete ctx when this function return false.
 */
BOOL D3dCtx::initialize(JNIEnv *env, jobject obj)
{
    HRESULT hr;
    //    int oldWidth, oldHeight;
    //    BOOL needBiggerRenderSurface = false;

    // It is possible that last time Emulation mode is used.
    // If this is the case we will try Hardware mode first.
    deviceInfo = setDeviceInfo(driverInfo, &bFullScreen, minZDepth, minZDepthStencil);

    if ((pD3D == NULL) || (driverInfo == NULL)) {
	return false;
    }
    /*
    if (offScreenWidth > driverInfo->desktopMode.Width) {
	if (debug) {
	    printf("OffScreen width cannot greater than %d\n",
		   driverInfo->desktopMode.Width);
	}
	oldWidth = offScreenWidth;
	offScreenWidth = driverInfo->desktopMode.Width;
	needBiggerRenderSurface = true;

    }

    if (offScreenHeight > driverInfo->desktopMode.Height) {
	if (debug) {
	    printf("OffScreen Height cannot greater than %d\n",
		   driverInfo->desktopMode.Height);
	}
	oldHeight = offScreenHeight;
	offScreenHeight = driverInfo->desktopMode.Height;
	needBiggerRenderSurface = true;
    }
    */

    if (!bFullScreen) {
	getScreenRect(hwnd, &savedClientRect);
	CopyMemory(&screenRect, &savedClientRect, sizeof (RECT));
    }

    dwBehavior = findBehavior();

    if (debug) {
	printf("[Java3D]: Use %s, ", driverInfo->adapterIdentifier.Description);

	if (deviceInfo->isHardwareTnL &&
	    (dwBehavior == D3DCREATE_SOFTWARE_VERTEXPROCESSING))
	{
	    // user select non-TnL device
	    printf("Hardware Rasterizer\n");
	} else {
	    printf("%s (TnL) \n",   deviceInfo->deviceName);
	}
    }

    setPresentParams(env, obj);

    if (debug) {
	printf("\n[Java3D]: Create device :\n");
	printInfo(&d3dPresent);
    }


    if ((d3dPresent.BackBufferWidth <= 0) ||
	(d3dPresent.BackBufferHeight <= 0)) {
	if (debug) {
	    printf("[Java3D]: D3D: Can't create device of buffer size %dx%d\n",
		   d3dPresent.BackBufferWidth,
		   d3dPresent.BackBufferHeight);
	}
	return false;
    }

if(bUseNvPerfHUD)
{
   // using NVIDIA NvPerfHUD profiler
   printf("\n[Java3D]: running in NVIDIA NvPerfHUD mode:");
   UINT adapterToUse=driverInfo->iAdapter;
   D3DDEVTYPE deviceType=deviceInfo->deviceType;
   DWORD behaviorFlags = dwBehavior;
   bool isHUDavail = false;

   // Look for 'NVIDIA NVPerfHUD' adapter
   // If it is present, override default settings
   for (UINT adapter=0;adapter<pD3D->GetAdapterCount(); adapter++)
   {
    D3DADAPTER_IDENTIFIER9 identifier;
    HRESULT Res=pD3D->GetAdapterIdentifier(adapter,0,&identifier);
    printf("\n Adapter identifier : %s",identifier.Description);
    if (strcmp(identifier.Description,"NVIDIA NVPerfHUD")==0)
	{
	     adapterToUse=adapter;
		 deviceType=D3DDEVTYPE_REF;
		 behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
         isHUDavail = true;
         printf("\n[Java3D]: found  a NVIDIA NvPerfHUD adapter");
		 break;
	}
   }
   hr = pD3D->CreateDevice( adapterToUse,
		                      deviceType,
							  topHwnd,
                              behaviorFlags,
                              &d3dPresent,
							  &pDevice);
   if(!FAILED(hr)&& isHUDavail)
   {
     printf("\n[Java3D]: Using NVIDIA NvPerfHUD ! \n");
   }
   else
   {
	    printf("\n[Java3D]: No suitable device found for NVIDIA NvPerfHUD ! \n");
   }
}
else
{
   // NORMAL Mode
     hr = pD3D->CreateDevice(driverInfo->iAdapter,
			    deviceInfo->deviceType,
			    topHwnd,
			    dwBehavior,
			    &d3dPresent,
			    &pDevice);
}


    if (FAILED(hr) && (requiredDeviceID < 0))
	{
      printf("\n[Java3D]: Using D3DDEVTYPE_REF mode.\n");
	if (deviceInfo->deviceType != D3DDEVTYPE_REF) {
	// switch to reference mode
	    warning(CREATEDEVICEFAIL, hr);
	    deviceInfo  = driverInfo->d3dDeviceList[DEVICE_REF];
	    dwBehavior = findBehavior();
	    deviceInfo->findDepthStencilFormat(minZDepth,minZDepthStencil);
	    d3dPresent.AutoDepthStencilFormat =
		deviceInfo->depthStencilFormat;
	    if (deviceInfo->depthStencilFormat == D3DFMT_UNKNOWN) {
		// should not happen since reference mode will
		// support all depth stencil format
		error(DEPTHSTENCILNOTFOUND);
		return false;
	    }
	    if (debug) {
		printf("[Java3D]: Fallback to create reference device :\n");
		printInfo(&d3dPresent);
	    }

    	    hr = pD3D->CreateDevice(driverInfo->iAdapter,
				    deviceInfo->deviceType,
				    topHwnd,
				    dwBehavior,
				    &d3dPresent,
				    &pDevice);
	}
    }

    /*
    if (offScreen && needBiggerRenderSurface) {
	IDirect3DSurface9 *pRenderTarget;
	IDirect3DSurface9 *pStencilDepthTarget;

	hr = pDevice->CreateRenderTarget(oldWidth,
					 oldHeight,
					 driverInfo->desktopMode.Format,
					 D3DMULTISAMPLE_NONE,
					 true,
					 &pRenderTarget);

	if (FAILED(hr)) {
	    printf("Fail to CreateRenderTarget %s\n", DXGetErrorString9(hr));
	} else {
	    hr = pDevice->CreateDepthStencilSurface(oldWidth,
						    oldHeight,
						    deviceInfo->depthStencilFormat,
						    D3DMULTISAMPLE_NONE,
						    &pStencilDepthTarget);
	    if (FAILED(hr)) {
		printf("Fail to CreateDepthStencilSurface %s\n", DXGetErrorString9(hr));
		pRenderTarget->Release();
	    } else {
		hr = pDevice->SetRenderTarget(pRenderTarget,
					     pStencilDepthTarget);
		if (FAILED(hr)) {
		    printf("Fail to SetRenderTarget %s\n", DXGetErrorString9(hr));
		    pRenderTarget->Release();
		    pStencilDepthTarget->Release();
		} else {
		    printf("Successfully set bigger buffer\n");
		}
	    }
	}
    }
    */


    setWindowMode();

    if (FAILED(hr)) {
	release();
	if (!inToggle) {
	    error(CREATEREFDEVICEFAIL, hr);
	} else {
	    warning(CREATEREFDEVICEFAIL, hr);
	}
	return false;
    }

    if (deviceInfo != NULL) {
	bindTextureIdLen = deviceInfo->maxTextureUnitStageSupport;
    } else {
	bindTextureIdLen = 1;
    }


     jclass canvasCls =  env->GetObjectClass(obj);
     jfieldID id;

    // TODO check it !!!!
	 id = env->GetFieldID(canvasCls, "maxTexCoordSets", "I"); //was numtexCoordSupported
    env->SetIntField(obj, id, TEXSTAGESUPPORT);

    if (bindTextureIdLen > 1) {
	if (bindTextureIdLen > TEXSTAGESUPPORT) {
	    // D3D only support max. 8 stages.
	    bindTextureIdLen = TEXSTAGESUPPORT;
	}
	multiTextureSupport = true;
	id = env->GetFieldID(canvasCls, "multiTexAccelerated", "Z");
	env->SetBooleanField(obj, id, JNI_TRUE);

	// TODO check it !!!!
	id = env->GetFieldID(canvasCls, "maxTextureUnits", "I"); //was numTexUnitSupported
	env->SetIntField(obj, id, bindTextureIdLen);
    } else {
	bindTextureIdLen = 1;
    }

    bindTextureId = (INT *) malloc(sizeof(INT) * bindTextureIdLen);
    if (bindTextureId == NULL) {
	release();
	error(OUTOFMEMORY);
	return false;
    }

    setViewport();
    setDefaultAttributes();

    createVertexBuffer();

    if (debug && (deviceInfo != NULL)) {
	if (multiTextureSupport) {
	    printf("Max Texture Unit Stage support : %d \n",
		   deviceInfo->maxTextureBlendStages);

	    printf("Max Simultaneous Texture unit support : %d \n",
		   deviceInfo->maxSimultaneousTextures);
	} else {
	    printf("MultiTexture support : false\n");
	}
    }
    return true;
}

// canvas size change, get new back buffer
INT D3dCtx::resize(JNIEnv *env, jobject obj)
{
    int retValue;

    if ((pDevice == NULL) || bFullScreen) {
	return false; // not yet ready when startup
    }

    if (forceResize) {
	// ignore first resize request after screen toggle
	forceResize = false;
	return NOCHANGE;
    }
    // we don't want resize to do twice but when window toggle
    // between fullscreen and window mode, the move event will got
    // first. Thus it will get size correctly without doing resize.

    BOOL moveRequest;


    GetWindowRect(hwnd, &windowRect);

    if ((windowRect.right == screenRect.right) &&
	(windowRect.left == screenRect.left) &&
	(windowRect.bottom == screenRect.bottom) &&
	(windowRect.top == screenRect.top)) {
	return NOCHANGE;
    }

    if (((windowRect.left - windowRect.right)
	 == (screenRect.left - screenRect.right)) &&
	((windowRect.bottom - windowRect.top)
	 == (screenRect.bottom - screenRect.top))) {
	moveRequest = true;
    } else {
	moveRequest = false;
    }


    HMONITOR oldMonitor = monitor;
    monitor =  findMonitor();

    getScreenRect(hwnd, &screenRect);

    if (monitor != oldMonitor) {
	enumDisplayMode(&devmode);
	setDriverInfo();
	release();
	initialize(env, obj);
	return RECREATEDDRAW;
    }

     if (!moveRequest) {

	 retValue = resetSurface(env, obj);
	 if (retValue != RECREATEDFAIL) {
	     return retValue;
	 } else {
	     return RECREATEDDRAW;
	 }
     }
     return NOCHANGE;
}


INT D3dCtx::toggleMode(BOOL _bFullScreen, JNIEnv *env, jobject obj)
{
    INT retValue;

    if ((pDevice == NULL) ||
	(!_bFullScreen &&
	 !deviceInfo->canRenderWindowed)) {
	// driver did not support window mode
	return NOCHANGE;
    }

    int onScreenCount = 0;

    for (ITER_D3dCtxVector p = d3dCtxList.begin(); p != d3dCtxList.end(); p++) {
	if (!(*p)->offScreen &&
	    //	    (monitor == (*p)->monitor) &&
	    (++onScreenCount > 1)) {
	    // don't toggle if there are more than one onScreen ctx exists
	    // in the same screen
	    return false;
	}
    }


    inToggle = true;
    bFullScreen = _bFullScreen;

    retValue = resetSurface(env, obj);

    if (retValue != RECREATEDFAIL) {
	forceResize = true;
    } else {
	// Switch back to window mode if fall to toggle fullscreen
	// and vice versa
	bFullScreen = !bFullScreen;
	release();
	if (initialize(env, obj)) {
	    retValue = RECREATEDDRAW;
	    forceResize = true;
	} else {
	    retValue = RECREATEDFAIL;
	}
    }
    if (retValue != RECREATEDFAIL) {
	setViewport();
    }

    inToggle = false;
    return retValue;
}

VOID D3dCtx::setPresentParams(JNIEnv *env, jobject obj)
{
    setCanvasProperty(env, obj);

    d3dPresent.AutoDepthStencilFormat = deviceInfo->depthStencilFormat;
    d3dPresent.EnableAutoDepthStencil = true;

    if ((antialiasing != UNNECESSARY) &&
	deviceInfo->supportAntialiasing()) {
	d3dPresent.MultiSampleType = deviceInfo->getBestMultiSampleType();
    } else {
	d3dPresent.MultiSampleType = D3DMULTISAMPLE_NONE;
    }
    d3dPresent.BackBufferCount = 1;
    d3dPresent.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

    // We can't use Discard, otherwise readRaster will fail as
    // content of backbuffer will discard after swap unless
    // we always call readRaster() just before swap.
    // However in this way we can't use multisample effect


    if (bFullScreen) {
	GetWindowRect(topHwnd, &savedTopRect);
	GetWindowRect(hwnd, &savedClientRect);

	d3dPresent.Windowed = false;
	d3dPresent.hDeviceWindow = topHwnd;

	if ((antialiasing != UNNECESSARY) &&
	    deviceInfo->supportAntialiasing()) {
	    d3dPresent.SwapEffect = D3DSWAPEFFECT_DISCARD;
	} else {
	    d3dPresent.SwapEffect = D3DSWAPEFFECT_FLIP;
	}
	d3dPresent.BackBufferWidth = driverInfo->desktopMode.Width;
	d3dPresent.BackBufferHeight = driverInfo->desktopMode.Height;
	d3dPresent.BackBufferFormat = driverInfo->desktopMode.Format;
	d3dPresent.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	if (deviceInfo->supportRasterPresImmediate)
	    d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	else
		d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    } else {
	d3dPresent.Windowed = true;
	d3dPresent.hDeviceWindow = hwnd;

	if ((antialiasing != UNNECESSARY) &&
	    deviceInfo->supportAntialiasing()) {
	    d3dPresent.SwapEffect = D3DSWAPEFFECT_DISCARD;
	} else {
	    d3dPresent.SwapEffect = D3DSWAPEFFECT_COPY;
	}
	d3dPresent.BackBufferWidth = getWidth();
	d3dPresent.BackBufferHeight = getHeight();
	d3dPresent.BackBufferFormat = driverInfo->desktopMode.Format;
	d3dPresent.FullScreen_RefreshRateInHz = 0;

	if (deviceInfo->supportRasterPresImmediate)
	    d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	else
		d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    }

}

INT D3dCtx::resetSurface(JNIEnv *env, jobject obj)
{
    D3dDeviceInfo* oldDevice = deviceInfo;
    HRESULT hr;


    deviceInfo = setDeviceInfo(driverInfo, &bFullScreen, minZDepth, minZDepthStencil);

    if (deviceInfo == NULL) {
	return NOCHANGE;
    }
    if (deviceInfo != oldDevice) {
	// we fall back to Reference mode last time,
	// try to see if we can run in hardware mode after
	// the surface size change.
	release();
	if (initialize(env, obj)) {
	    return RECREATEDDRAW;
	} else {
	    return RECREATEDFAIL;
	}
    } else {
	setPresentParams(env, obj);
	if (debug) {
	    printf("\nReset Device :\n");
	    printInfo(&d3dPresent);
	}

	// Must release any non default pool surface, otherwise
	// Reset() will fail
	SafeRelease(depthStencilSurface);
	SafeRelease(frontSurface);
	SafeRelease(backSurface);

	releaseVB();
	SafeRelease(lineModeIndexBuffer);
	quadIndexBufferSize = 0;

	hr = pDevice->Reset(&d3dPresent);
	if (FAILED(hr)) {
	    warning(RESETFAIL, hr);
	    // try to recreate Surface, if still fail, try Reference mode
	    release();
	    if (initialize(env, obj)) {
		return RECREATEDDRAW;
	    } else {
		return RECREATEDFAIL;
	    }
	} else {
	    setWindowMode();
	    setDefaultAttributes();
	    return RESETSURFACE;
	}
    }

    return NOCHANGE;
}


VOID D3dCtx::error(int idx)
{
     error(getErrorMessage(idx));
}

VOID D3dCtx::error(int idx, HRESULT hr)
{
    error(getErrorMessage(idx), hr);
}


VOID D3dCtx::warning(int idx)
{
    printf("[Java3D] Warning : %s\n", getErrorMessage(idx));
}

VOID D3dCtx::warning(int idx, HRESULT hr)
{
    printf("[Java3D] Warning : %s - %s\n", getErrorMessage(idx), DXGetErrorString9(hr));
}


VOID D3dCtx::error(char *s)
{
    showError(hwnd, s, bFullScreen);
}

VOID D3dCtx::error(char *s, HRESULT hr)
{
    char message[400];
    sprintf(message, "%s - %s", s, DXGetErrorString9(hr));
    showError(hwnd, message, bFullScreen);
}


VOID D3dCtx::d3dWarning(int idx)
{
    printf("[Java3D] Warning: %s\n", getErrorMessage(idx));
}

VOID D3dCtx::d3dWarning(int idx, HRESULT hr)
{
    printf("[Java3D] Warning: %s - %s\n",
	   getErrorMessage(idx), DXGetErrorString9(hr));

}

VOID D3dCtx::d3dError(char *s)
{
    showError(GetDesktopWindow(), s, false);
}


VOID D3dCtx::d3dError(int idx)
{
    d3dError(getErrorMessage(idx));
}


// Only display message box for the first error, since
// Java3D will continue to invoke createContext() when it fail
VOID D3dCtx::showError(HWND hwnd, char *s, BOOL bFullScreen)
{
    if (firstError) {
	firstError = false;
	if (bFullScreen) {
	    // In full screen mode, we can't see message box
	    printf("[Java3D] Error: %s\n", s);
	    exit(1);
	} else {
	    MessageBox(hwnd, s, "Java 3D",  MB_OK|MB_ICONERROR);
	}
    }
}


DWORD D3dCtx::getWidth()
{
    if (!offScreen) {
	return screenRect.right - screenRect.left;
    } else {
	return offScreenWidth;
    }

}


DWORD D3dCtx::getHeight()
{
    if (!offScreen) {
	return screenRect.bottom - screenRect.top;
    } else {
	return offScreenHeight;
    }

}

D3dDeviceInfo* D3dCtx::selectDevice(int deviceID,
				                    D3dDriverInfo *driverInfo,
				                    BOOL *bFullScreen,
				                    int minZDepth,
					                int minZDepthStencil)
{
    D3dDeviceInfo *pDevice;

    for (int i=0; i < numDeviceTypes; i++) 
	{
	pDevice = driverInfo->d3dDeviceList[i];
	if ((((deviceID == DEVICE_HAL) || (deviceID == DEVICE_HAL_TnL)) &&
	     (pDevice->deviceType == D3DDEVTYPE_HAL)) ||
	     (deviceID == DEVICE_REF) &&
	     (pDevice->deviceType == D3DDEVTYPE_REF)) 
	{
	    if ((*bFullScreen && !pDevice->fullscreenCompatible) ||
		    (!*bFullScreen && !pDevice->desktopCompatible)) 
		{
		  if (pDevice->deviceType == D3DDEVTYPE_HAL) 
		     {
		       d3dError(HALDEVICENOTFOUND);
		     } 
		  else 
		    {
		      // should not happen, REF device always support
		      d3dError(DEVICENOTFOUND);
		    }
		  exit(1);
	    }
	  if (pDevice->maxZBufferDepthSize == 0) 
	    {
		  if (pDevice->deviceType == D3DDEVTYPE_HAL) 
		  {
		     d3dError(HALNOTCOMPATIBLE);
		  } 
		  else 
		  {
		    // should not happen, REF device always support
		    d3dError(DEVICENOTFOUND);
		  }
		  exit(1);
	    }
	   if (pDevice->deviceType == D3DDEVTYPE_HAL) 
	   {
		 if ((deviceID == DEVICE_HAL_TnL) &&
		    !pDevice->isHardwareTnL) 
		  {
		    d3dError(TNLHALDEVICENOTFOUND);
		    exit(1);
		  }
	   }

	    pDevice->findDepthStencilFormat(minZDepth, minZDepthStencil);
	    if (pDevice->depthStencilFormat == D3DFMT_UNKNOWN) 
		{
		  d3dError(DEPTHSTENCILNOTFOUND);
		  exit(1);
	    }
	    return pDevice;
	}
    }

    // should not happen
    d3dError(DEVICENOTFOUND);
    exit(1);
}



D3dDeviceInfo* D3dCtx::selectBestDevice(D3dDriverInfo *driverInfo,
					                    BOOL *bFullScreen, 
										int minZDepth, 
										int minZDepthStencil)
{
    D3dDeviceInfo *pDevice;
    D3dDeviceInfo *bestDevice = NULL;
    int i;

    for (i=0; i < numDeviceTypes; i++) 
	{
	pDevice = driverInfo->d3dDeviceList[i];
	if (pDevice->maxZBufferDepthSize > 0) 
	{
	    pDevice->findDepthStencilFormat(minZDepth, minZDepthStencil);

	    if (pDevice->depthStencilFormat == D3DFMT_UNKNOWN) 
		{
		if (pDevice->deviceType == D3DDEVTYPE_REF) 
		{
		    d3dError(DEPTHSTENCILNOTFOUND);
		    return NULL;
		} else {
		    continue;
		}
	    }
	    if (*bFullScreen) {
		if (pDevice->fullscreenCompatible) {
		    bestDevice = pDevice;
		    break;
		}
	    } else {
		if (pDevice->canRenderWindowed) {
		    if (pDevice->desktopCompatible) {
			bestDevice = pDevice;
			break;
		    }
		} else {
		    if (pDevice->fullscreenCompatible) {
			// switch to fullscreen mode
			*bFullScreen = true;
			bestDevice = pDevice;
			break;
		    }
		}
	    }
	}
    }

    if (bestDevice == NULL) {
	// should not happen
	d3dError(DEVICENOTFOUND);
	return NULL;
    }

    // TODO: suggest another display mode for user
    /*
    if (bestDevice->deviceType == D3DDEVTYPE_REF) {
	// Recomend other display mode that support
	// hardware accerated rendering if any.
	int numModes = pD3D->GetAdapterModeCount(driverInfo->iAdapter);
	D3DDISPLAYMODE dmode;

	for (i=0; i < numModes; i++) {
	    pD3D->EnumAdapterModes(pDriverInfo->iAdapter, i, &dmode);
	    if ((dmode.Width < 640) || (dmode.Height < 400)) {
		// filter out low resolution modes
		continue;
	    }
	}
	....
    }
    */
    return bestDevice;
}


VOID D3dCtx::setDeviceFromProperty(JNIEnv *env)
{

    jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

    if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( "j3d.d3ddevice" );
            jstring property = reinterpret_cast<jstring>(
					 env->CallStaticObjectMethod(
					     systemClass, method, name ));
	    jboolean isCopy;

            if ( property != NULL )
            {
                const char* chars = env->GetStringUTFChars(
					   property, &isCopy );
                if ( chars != 0 )
                {
		    if (stricmp(chars, "reference") == 0) {
			// There is no emulation device anymore in v8.0
			requiredDeviceID = DEVICE_REF;
		    } else if (stricmp(chars, "hardware") == 0) {
			requiredDeviceID = DEVICE_HAL;
		    } else if (stricmp(chars, "TnLhardware") == 0) {
			requiredDeviceID = DEVICE_HAL_TnL;
		    } else {
			d3dError(UNKNOWNDEVICE);
			exit(1);
		    }
                    env->ReleaseStringUTFChars( property, chars );
                }
            }
	    name = env->NewStringUTF( "j3d.d3ddriver" );
	    property = reinterpret_cast<jstring>(
				 env->CallStaticObjectMethod(
				     systemClass, method, name ));
            if ( property != NULL )
            {
                const char* chars = env->GetStringUTFChars(
					   property, &isCopy);
		 if ( chars != 0 )
                {
		    // atoi() return 0, our default value, on error.
		    requiredDriverID = atoi(chars);
		}
	    }
	}
    }
}

VOID D3dCtx::setFullScreenFromProperty(JNIEnv *env)
{

    jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

    bFullScreenRequired = false;
    bFullScreen = false;

    if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( "j3d.fullscreen" );
            jstring property = reinterpret_cast<jstring>(
                env->CallStaticObjectMethod(
                    systemClass, method, name ));
            if ( property != NULL )
            {
                jboolean isCopy;
                const char * chars = env->GetStringUTFChars(
                    property, &isCopy );
                if ( chars != 0 )
                {
                    if ( stricmp( chars, "required" ) == 0 ) {
                        bFullScreenRequired = true;
			bFullScreen = true;
                    } else if ( stricmp( chars, "preferred" ) == 0 ) {
			bFullScreen = true;
		    }
                    // "UNNECESSARY" is the default
                    env->ReleaseStringUTFChars( property, chars );
                }
            }
	}
    }

}

VOID D3dCtx::setVBLimitProperty(JNIEnv *env)
{
    jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

    if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( "j3d.vertexbufferlimit" );
            jstring property = reinterpret_cast<jstring>(
                env->CallStaticObjectMethod(
                    systemClass, method, name ));
            if ( property != NULL )
            {
                jboolean isCopy;
                const char * chars = env->GetStringUTFChars(
                    property, &isCopy );
                if ( chars != 0 )
                {
		    long vbLimit = atol(chars);
                    env->ReleaseStringUTFChars( property, chars );
		    if (vbLimit >= 6) {
			// Has to be at least 6 since for Quad the
			// limit reset to 2*vbLimit/3 >= 4
			printf("Java 3D: VertexBuffer limit set to %ld\n", vbLimit);
			vertexBufferMaxVertexLimit = vbLimit;
		    } else {
			printf("Java 3D: VertexBuffer limit should be an integer >= 6 !\n");
		    }

                }
            }
	}
    }
}

VOID D3dCtx::setDebugProperty(JNIEnv *env)
{
    jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

    debug = false;

    if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( "j3d.debug" );
            jstring property = reinterpret_cast<jstring>(
                env->CallStaticObjectMethod(
                    systemClass, method, name ));
            if ( property != NULL )
            {
                jboolean isCopy;
                const char * chars = env->GetStringUTFChars(
                    property, &isCopy );
                if ( chars != 0 )
                {
                    if ( stricmp( chars, "true" ) == 0 ) {
                        debug = true;
                    } else {
			debug = false;
		    }
                    // "UNNECESSARY" is the default
                    env->ReleaseStringUTFChars( property, chars );
                }
            }
	}
    }
}

BOOL D3dCtx::getSystemProperty(JNIEnv *env, char *strName, char *strValue)
{
    jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

    if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( strName );
            jstring property = reinterpret_cast<jstring>(
                env->CallStaticObjectMethod(
                    systemClass, method, name ));
            if ( property != NULL )
            {
                jboolean isCopy;
                const char * chars = env->GetStringUTFChars(property, &isCopy );
                if ( chars != 0 && stricmp( chars, strValue ) == 0 )
					{
						env->ReleaseStringUTFChars( property, chars );
                        return true;
                    }
					else
					{
                       env->ReleaseStringUTFChars( property, chars );
			           return false;
		            }
            }
	    }
    }
	return false;
}

VOID D3dCtx::setImplicitMultisamplingProperty(JNIEnv *env)
{
    jclass cls = env->FindClass("javax/media/j3d/VirtualUniverse");

    if (cls == NULL) {
	implicitMultisample = false;
	return;
    }

    jfieldID fieldID = env->GetStaticFieldID(cls, "mc", "Ljavax/media/j3d/MasterControl;");

    if (fieldID == NULL) {
	implicitMultisample = false;
	return;
    }

    jobject obj = env->GetStaticObjectField(cls, fieldID);

    if (obj == NULL) {
	implicitMultisample = false;
	return;
    }

    cls = env->FindClass("javax/media/j3d/MasterControl");

    if (cls == NULL) {
	implicitMultisample = false;
	return;
    }

    fieldID = env->GetFieldID(cls, "implicitAntialiasing", "Z");

    if (fieldID == NULL ) {
	implicitMultisample = false;
	return;
    }

    implicitMultisample = env->GetBooleanField(obj, fieldID);
    return;
}


// Callback to notify Canvas3D which mode it is currently running
VOID D3dCtx::setCanvasProperty(JNIEnv *env, jobject obj)
{
    int mask = javax_media_j3d_Canvas3D_EXT_ABGR |
	           javax_media_j3d_Canvas3D_EXT_BGR;

      if ((deviceInfo->depthStencilFormat == D3DFMT_D24S8) ||
	(deviceInfo->depthStencilFormat == D3DFMT_D24X4S4)) 
	{
	// The other format D3DFMT_D15S1 with 1 bit
	// stencil buffer has no use for Decal group so it
	// is ignored.
	mask |= javax_media_j3d_Canvas3D_STENCIL_BUFFER;   
    }
	

    jclass canvasCls =  env->GetObjectClass(obj);
    jfieldID id = env->GetFieldID(canvasCls, "fullScreenMode", "Z");
    env->SetBooleanField(obj, id, bFullScreen);

    id = env->GetFieldID(canvasCls, "fullscreenWidth", "I");
    env->SetIntField(obj, id, driverInfo->desktopMode.Width);

    id = env->GetFieldID(canvasCls, "fullscreenHeight", "I");
    env->SetIntField(obj, id, driverInfo->desktopMode.Height);

	//id = env->GetFieldID(canvasCls, "userStencilAvailable", "Z");
    //env->SetBooleanField(obj, id, deviceInfo->supportStencil );

    id = env->GetFieldID(canvasCls, "textureExtendedFeatures", "I");
    env->SetIntField(obj, id, deviceInfo->getTextureFeaturesMask());

    id = env->GetFieldID(canvasCls, "extensionsSupported", "I");
    env->SetIntField(obj, id, mask);


    id = env->GetFieldID(canvasCls, "nativeGraphicsVersion", "Ljava/lang/String;");
    char *version = "DirectX 9.0 or above";
    env->SetObjectField(obj, id, env->NewStringUTF(version));

    float degree = float(deviceInfo->maxAnisotropy);
    id = env->GetFieldID(canvasCls, "anisotropicDegreeMax", "F");
    env->SetFloatField(obj, id, degree);

    id = env->GetFieldID(canvasCls, "textureWidthMax", "I");
    env->SetIntField(obj, id, deviceInfo->maxTextureWidth);

    id = env->GetFieldID(canvasCls, "textureHeightMax", "I");
    env->SetIntField(obj, id, deviceInfo->maxTextureHeight);

    if (deviceInfo->maxTextureDepth > 0) {
	id = env->GetFieldID(canvasCls, "texture3DWidthMax", "I");
	env->SetIntField(obj, id, deviceInfo->maxTextureWidth);

	id = env->GetFieldID(canvasCls, "texture3DHeightMax", "I");
	env->SetIntField(obj, id, deviceInfo->maxTextureHeight);

	id = env->GetFieldID(canvasCls, "texture3DDepthMax", "I");
	env->SetIntField(obj, id, deviceInfo->maxTextureDepth);

	// issue 135
	// private String nativeGraphicsVendor = "<UNKNOWN>";
    // private String nativeGraphicsRenderer = "<UNKNOWN>";
	id = env->GetFieldID(canvasCls, "nativeGraphicsVendor", "Ljava/lang/String;");
	//char *nGVendor = driverInfo->adapterIdentifier.DeviceName ;
      char *nGVendor = deviceInfo->deviceVendor ;
      env->SetObjectField(obj, id, env->NewStringUTF(nGVendor));
	//  printf("DEBUG vendor : %s ", nGVendor);

	id = env->GetFieldID(canvasCls, "nativeGraphicsRenderer", "Ljava/lang/String;");
	//	    char *nGRenderer = driverInfo->adapterIdentifier.Description;
        char *nGRenderer = deviceInfo->deviceRenderer;
	    env->SetObjectField(obj, id, env->NewStringUTF(nGRenderer));


    }
}




VOID D3dCtx::createVertexBuffer()
{
    if (srcVertexBuffer != NULL) {
	// Each pDevice has its own vertex buffer,
	// so if different pDevice create vertex buffer has to
	// recreate again.
	srcVertexBuffer->Release();
    }

    if (dstVertexBuffer != NULL) {
	dstVertexBuffer->Release();
    }
    DWORD usage_D3DVERTEX = D3DUSAGE_DONOTCLIP|
				            D3DUSAGE_WRITEONLY|
				            D3DUSAGE_SOFTWAREPROCESSING;

	DWORD usage_D3DTLVERTEX= D3DUSAGE_DONOTCLIP|
		                     D3DUSAGE_SOFTWAREPROCESSING;

//# if NVIDIA_DEBUG
	if(deviceInfo->isHardwareTnL)
    {
	// remove SoftwareProcessing
    usage_D3DVERTEX = D3DUSAGE_DONOTCLIP|
				      D3DUSAGE_WRITEONLY;

	usage_D3DTLVERTEX = D3DUSAGE_DONOTCLIP;
   }
// # endif

    HRESULT hr =
	pDevice->CreateVertexBuffer(sizeof(D3DVERTEX),
				    usage_D3DVERTEX,
				    D3DFVF_XYZ,
				    D3DPOOL_MANAGED,
				    &srcVertexBuffer,
					NULL);

    if (FAILED(hr)) {
    printf("\n[Java3D] Failed to create VertexBuffer (D3DVERTEX)\n");
	error(CREATEVERTEXBUFFER, hr);

    }

    hr = pDevice->CreateVertexBuffer(sizeof(D3DTLVERTEX),
				     usage_D3DTLVERTEX,
				     D3DFVF_XYZRHW|D3DFVF_TEX1,
				     D3DPOOL_MANAGED,
				     &dstVertexBuffer,NULL);
    if (FAILED(hr)) {
	printf("\n[Java3D] Warning: Failed to create VertexBuffer (D3DTLVERTEX)\n");
	error(CREATEVERTEXBUFFER, hr);
    }
}


VOID D3dCtx::transform(D3DVERTEX *worldCoord, D3DTLVERTEX *screenCoord) {
    D3DVERTEX *pv;
    D3DTLVERTEX *tlpv;
    HRESULT hr;

    if (srcVertexBuffer != NULL) {
	// Need to disable Texture state, otherwise
	// ProcessVertices() will fail with debug message :
	//
	// "Number of output texture coordintes and their format should
	//  be the same in the destination vertex buffer and as
	//  computed for current D3D settings."
	//
	// when multiple texture is used.
	DWORD texState;
	// save original texture state
	pDevice->GetTextureStageState(0, D3DTSS_COLOROP, &texState);
	// disable texture processing
	pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);

	if (!softwareVertexProcessing) {
	    // ProcessVertices() only work in software vertex
	    // processing mode
	    //pDevice->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, TRUE);
		pDevice->SetSoftwareVertexProcessing(TRUE);
	}

	pDevice->SetRenderState(D3DRS_CLIPPING, FALSE);
	hr = srcVertexBuffer->Lock(0, 0, (VOID **)&pv, 0);
	if (FAILED(hr)) {
	    if (debug) {
		printf("[Java3D] Fail to lock buffer %s\n", DXGetErrorString9(hr));
	    }
	} else {
	    pv[0].x = worldCoord->x;
	    pv[0].y = worldCoord->y;
	    pv[0].z = worldCoord->z;

	    srcVertexBuffer->Unlock();
	    pDevice->SetStreamSource(0, srcVertexBuffer,0, sizeof(D3DVERTEX));

	    //pDevice->SetVertexShader(D3DFVF_XYZ);
		 pDevice->SetVertexShader(NULL);
	     pDevice->SetFVF(D3DFVF_XYZ);

	    hr = pDevice->ProcessVertices(0, 0, 1,
					  dstVertexBuffer,
					  NULL,
					  0);

	    if (FAILED(hr)) {
		if (debug) {
		    printf("[Java3D] Fail to processVertices %s\n", DXGetErrorString9(hr));
		}
	    } else {
		hr = dstVertexBuffer->Lock(0, 0, (VOID **)&tlpv,  D3DLOCK_READONLY);
		if (SUCCEEDED(hr)) {
		    screenCoord->sx = tlpv[0].sx;
		    screenCoord->sy = tlpv[0].sy;
		    screenCoord->sz = tlpv[0].sz;
		    screenCoord->rhw = tlpv[0].rhw;
		    dstVertexBuffer->Unlock();
		} else {
		    if (debug) {
			error("Fail to lock surface in transform", hr);
		    }
		}
	    }
	}
	pDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
	if (!softwareVertexProcessing) {
	    //pDevice->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, FALSE);
		pDevice->SetSoftwareVertexProcessing(FALSE);
	}
	// restore original texture state
	pDevice->SetTextureStageState(0, D3DTSS_COLOROP, texState);
    }
}



VOID D3dCtx::getScreenRect(HWND hwnd, RECT *rect) {

    GetWindowRect(hwnd, rect);

    if ((deviceInfo->isHardware) &&
	(numDriver > 1)) {

	MONITORINFO info;
	HMONITOR hMonitor = driverInfo->hMonitor;

	info.cbSize = sizeof(MONITORINFO);
	if (hMonitor == NULL) {
	    hMonitor = MonitorFromWindow(hwnd,
					 MONITOR_DEFAULTTONEAREST);
	}
	GetMonitorInfo(hMonitor, &info);
	monitorLeft = info.rcMonitor.left;
	monitorTop = info.rcMonitor.top;
	rect->left -= monitorLeft;
	rect->right -= monitorLeft;
	rect->top -= monitorTop;
	rect->bottom -= monitorTop;
    } else {
	monitorLeft = 0;
	monitorTop = 0;
    }
}

/*
 * Search the monitor that this window competely enclosed.
 * Return NULL if this window intersect several monitor
 */

HMONITOR D3dCtx::findMonitor()
{

    if ((osvi.dwMajorVersion < 4) ||
	(numDriver < 2)) {
	return NULL;
    }

    RECT rect;
    MONITORINFO info;
    HMONITOR hmonitor = MonitorFromWindow(hwnd,
					  MONITOR_DEFAULTTONEAREST);
    info.cbSize = sizeof(MONITORINFO);
    GetMonitorInfo(hmonitor, &info);
    GetWindowRect(hwnd, &rect);

    if ((info.rcMonitor.left <= rect.left) &&
	(info.rcMonitor.right >= rect.right) &&
	(info.rcMonitor.top <= rect.top) &&
	(info.rcMonitor.bottom >= rect.bottom)) {
	if (info.dwFlags & MONITORINFOF_PRIMARY) {
	    // Pass NULL is same as passing the guid of the
	    // first monitor. This can avoid recreate when
	    // window drag between screen borders from first
	    // screen.
	    return NULL;
	} else {
	    return hmonitor;
	}
    }
    return NULL;
}


D3dDeviceInfo* D3dCtx::setDeviceInfo(D3dDriverInfo *driverInfo,
				     BOOL *bFullScreen,
				     int minZDepth,
					 int minZDepthStencil)
{
    if (requiredDeviceID >= 0) 
	{
	 return selectDevice(requiredDeviceID, 
		                 driverInfo,
			             bFullScreen, 
					     minZDepth,
						 minZDepthStencil);
    } 
	else 
	{
	 return selectBestDevice(driverInfo, 
		                     bFullScreen,
				             minZDepth,
							 minZDepthStencil);
    }
}

// Find the adapter that this window belongs to
// and set driverInfo to this
VOID D3dCtx::setDriverInfo()
{
    D3dDriverInfo *newDriver = NULL;

    if (requiredDriverID <= 0) {
	if ((numDriver < 2) ||
	    (monitor == NULL) ||
	    (osvi.dwMajorVersion < 4)) {
	    // windows 95 don't support multiple monitors
	    // Use Primary display driver
	    newDriver = d3dDriverList[0];
	} else {
	    for (int i=0; i < numDriver; i++) {
		if (d3dDriverList[i]->hMonitor == monitor) {
		    newDriver = d3dDriverList[i];
		    break;
		}
	    }
	}
    } else {
	if (requiredDriverID > numDriver) {
	    requiredDriverID = numDriver;
	}
	newDriver = d3dDriverList[requiredDriverID-1];
    }

    driverInfo = newDriver;
}


VOID D3dCtx::setDefaultAttributes()
{

    /*pDevice->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING,
			    softwareVertexProcessing);*/
	pDevice->SetSoftwareVertexProcessing(softwareVertexProcessing);

    pDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE,
			    D3DMCS_MATERIAL);
    pDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE,
			    D3DMCS_MATERIAL);
    pDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
    // Default specular is FALSE
    pDevice->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
    // Texture & CULL mode  default value for D3D is different from OGL

    pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

    // Default in D3D is D3DCMP_LESSEQUAL, OGL is D3DCMP_LESS

    // Set Range based fog
    pDevice->SetRenderState(D3DRS_RANGEFOGENABLE,
			    deviceInfo->rangeFogEnable);

    // disable antialiasing (default is true in D3D)
    if (!implicitMultisample) {
	pDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, FALSE);
    }

    pointSize = 1;
    cullMode = D3DCULL_CW;
    fillMode = D3DFILL_SOLID;
    zWriteEnable = TRUE;
    zEnable = TRUE;

    if ((pDevice != NULL) && (bindTextureId != NULL)) {
	for (int i=0; i < bindTextureIdLen; i++) {
	    pDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
	    pDevice->SetTextureStageState(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	    pDevice->SetTextureStageState(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	    pDevice->SetTextureStageState(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	    //pDevice->SetTextureStageState(i, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
	    //pDevice->SetTextureStageState(i, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	    bindTextureId[i] = -1;
	}
    }


    for (int i=0; i < TEXSTAGESUPPORT; i++) {
	texGenMode[i] = TEX_GEN_NONE;
	texTransformSet[i] = false;
	texCoordFormat[i] = 2;
	texTransform[i] = identityMatrix;
	texStride[i] = 0;
	texTranslateSet[i] = false;
    }
}

VOID D3dCtx::enumDisplayMode(DEVMODE* dmode)
{

    MONITORINFOEX mi;

    if (monitor == NULL) {
	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, dmode );
    } else {
	mi.cbSize = sizeof(MONITORINFOEX);
	GetMonitorInfo(monitor, (MONITORINFOEX *) &mi);
	dmode->dmSize = sizeof(DEVMODE);
	EnumDisplaySettings( mi.szDevice, ENUM_CURRENT_SETTINGS, dmode);
    }
}
// check what kind of Vertex processing will be used
DWORD D3dCtx::findBehavior()
{
	bForceHwdVertexProcess = getSystemProperty(jniEnv,"j3d.d3dVertexProcess","hardware");
	bForceMixVertexProcess = getSystemProperty(jniEnv,"j3d.d3dVertexProcess","mixed");
	bForceSWVertexProcess = getSystemProperty(jniEnv,"j3d.d3dVertexProcess","software");

	bUseNvPerfHUD = getSystemProperty(jniEnv,"j3d.useNvPerfHUD","true");

	if (bUseNvPerfHUD) // must have bForceHwdVertexProcess as true
	{
		printf("\n[Java3D]: using j3d.useNvPerfHUD=true\n");
     bForceHwdVertexProcess = true;
	 bForceMixVertexProcess =  false;
	 bForceSWVertexProcess = false;
	}

    if (bForceHwdVertexProcess)
	{
      softwareVertexProcessing = FALSE;
	  printf("\n[Java3D]: using d3dVertexProcess=hardware\n");
	  return D3DCREATE_HARDWARE_VERTEXPROCESSING;
	}

	if (bForceMixVertexProcess)
	{
      printf("\n[Java3D]: using d3dVertexProcess=mixed\n");
      softwareVertexProcessing = FALSE;
	  return D3DCREATE_MIXED_VERTEXPROCESSING;
	}

	if (bForceSWVertexProcess)
	{
      printf("\n[Java3D]: using d3dVertexProcess=software\n");
      softwareVertexProcessing = TRUE;
	  return D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}
	// high-end video cards: NV25 and above
	if (deviceInfo->isHardwareTnL && deviceInfo->supportShaders11 &&
	     ((requiredDeviceID < 0) || (requiredDeviceID == DEVICE_HAL_TnL)))
	   {
        if (debug){printf("\n[Java3D]: using hardware Vertex Processing.\n");}
	    softwareVertexProcessing = FALSE;
	    return D3DCREATE_HARDWARE_VERTEXPROCESSING;
       }
    // midle-end video cards
    if (deviceInfo->isHardwareTnL &&
	   ((requiredDeviceID < 0) || (requiredDeviceID == DEVICE_HAL_TnL)))
	   {
        if (debug){printf("\n[Java3D]: using mixed Vertex Processing.\n");}
	    softwareVertexProcessing = FALSE;
	    return D3DCREATE_MIXED_VERTEXPROCESSING;
       }
	 else // low-end vcards use Software Vertex Processing
	  {
		if (debug){printf("\n[Java3D]: using software Vertex Processing.\n");}
        softwareVertexProcessing = TRUE;
	    return D3DCREATE_SOFTWARE_VERTEXPROCESSING;
      }
}

VOID D3dCtx::printInfo(D3DPRESENT_PARAMETERS *d3dPresent)
{

    if (d3dPresent->Windowed) {
	printf("Window ");
    } else {
	printf("FullScreen ");
    }

	printf("%dx%d %s, handle=%x, MultiSampleType %s, SwapEffect %s, AutoDepthStencilFormat: %s\n",
	   d3dPresent->BackBufferWidth,
	   d3dPresent->BackBufferHeight,
	   getPixelFormatName(d3dPresent->BackBufferFormat),
	   d3dPresent->hDeviceWindow,
	   getMultiSampleName(d3dPresent->MultiSampleType),
	   getSwapEffectName(d3dPresent->SwapEffect),
	   getPixelFormatName(d3dPresent->AutoDepthStencilFormat));
}

VOID D3dCtx::setWindowMode()
{
    if (inToggle) {
	if (!bFullScreen) {
	    SetWindowLong(topHwnd, GWL_STYLE, winStyle);
	    SetWindowPos(topHwnd, HWND_NOTOPMOST, savedTopRect.left, savedTopRect.top,
			 savedTopRect.right - savedTopRect.left,
			 savedTopRect.bottom - savedTopRect.top,
			 SWP_SHOWWINDOW);
	} else {
	    SetWindowLong(topHwnd, GWL_STYLE,
			  WS_POPUP|WS_SYSMENU|WS_VISIBLE);
	}
    }

}

VOID D3dCtx::setAmbientLightMaterial()
{
    // We need to set a constant per vertex color
    // There is no way in D3D to do this. It is workaround
    // by adding Ambient light and set Ambient Material
    // color temporary
    pDevice->GetLight(0, &savedLight);
    pDevice->GetMaterial(&savedMaterial);
    pDevice->GetLightEnable(0, &savedLightEnable);

    CopyColor(ambientMaterial.Ambient,
	      currentColor_r,
	      currentColor_g,
	      currentColor_b,
	      currentColor_a);


    // This is what the specification say it should set
    ambientMaterial.Specular.a = currentColor_a;

    // This is what we found after testing - spec. is not correct
    ambientMaterial.Diffuse.a = currentColor_a;

    pDevice->SetLight(0, &ambientLight);
    pDevice->SetMaterial(&ambientMaterial);
    pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    pDevice->LightEnable(0, TRUE);
}

VOID D3dCtx::restoreDefaultLightMaterial()
{
    // restore original values after setAmbientLightMaterial()
    pDevice->SetLight(0, &savedLight);
    pDevice->SetMaterial(&savedMaterial);
    pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    pDevice->LightEnable(0, savedLightEnable);
}

VOID D3dCtx::freeVBList(D3dVertexBufferVector *v)
{
    //LPD3DVERTEXBUFFER *p;
	LPD3DVERTEXBUFFER r;

    lockGeometry();

   for (ITER_LPD3DVERTEXBUFFER p = v->begin(); p != v->end(); ++p) {
	// Remove itself from current ctx  vertexBufferTable list
	r = (*p)->next;
	if (r != NULL) {
	    r->previous = (*p)->previous;
	}
	(*p)->previous->next = r;
	// Now we can free current VB
	delete (*p);
    }
    v->clear();
    unlockGeometry();
}


VOID D3dCtx::freeResourceList(LPDIRECT3DRESOURCE9Vector *v)
{
    ITER_LPDIRECT3DRESOURCE9 s;

    lockSurfaceList();
    for (s = v->begin(); s != v->end(); ++s) {
	(*s)->Release();
    }
    v->clear();
    unlockSurfaceList();
}

VOID D3dCtx::freeList()
{
    if (useFreeList0) {
	if (freeResourceList1.size() > 0) {
	    freeResourceList(&freeResourceList1);
	}
	if (freeVBList1.size() > 0) {
	    freeVBList(&freeVBList1);
	}
	useFreeList0 = false;
    } else {
	if (freeResourceList0.size() > 0) {
	    freeResourceList(&freeResourceList0);
	}
	if (freeVBList0.size() > 0) {
	    freeVBList(&freeVBList0);
	}
	useFreeList0 = true;
    }
}

VOID D3dCtx::freeVB(LPD3DVERTEXBUFFER vb)
{
    if (vb != NULL) {
	lockSurfaceList();
	if (useFreeList0) {
	    freeVBList0.push_back(vb);
	} else {
	    freeVBList1.push_back(vb);
	}
	unlockSurfaceList();
    }
}


VOID D3dCtx::freeResource(LPDIRECT3DRESOURCE9 res)
{
    if (res != NULL) {
	lockSurfaceList();
	if (useFreeList0) {
	    freeResourceList0.push_back(res);
	} else {
	    freeResourceList1.push_back(res);
	}
	unlockSurfaceList();
    }
}

BOOL D3dCtx::createFrontBuffer()
{
    HRESULT hr;

	/*CreateImageSurface*/
    hr = pDevice->CreateOffscreenPlainSurface(
		             driverInfo->desktopMode.Width,
				     driverInfo->desktopMode.Height,
				     D3DFMT_A8R8G8B8,
					 D3DPOOL_SCRATCH,
				     &frontSurface,
					 NULL);
    if (FAILED(hr)) {
	if (debug) {
	    printf("[Java3D] Fail to CreateOffscreenPlainSurface %s\n",
		   DXGetErrorString9(hr));
	}
	frontSurface = NULL;
	return false;
    }
    return true;
}

jboolean  D3dCtx::getJavaBoolEnv(JNIEnv *env, char* envStr)
{
   jclass systemClass = env->FindClass( "javax/media/j3d/MasterControl" );

   if ( systemClass != NULL )
    {
        jmethodID method = env->GetStaticMethodID(
            systemClass, "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;" );
        if ( method != NULL )
        {
            jstring name = env->NewStringUTF( envStr );
            jstring property = reinterpret_cast<jstring>(
                env->CallStaticObjectMethod(
                    systemClass, method, name ));
            if ( property != NULL )
            {
                jboolean isCopy;
                const char * chars = env->GetStringUTFChars(
                    property, &isCopy );
                if ( chars != 0 )
                {
                    if ( stricmp( chars, "true" ) == 0 )
					{
                        env->ReleaseStringUTFChars( property, chars );
						return true;
                    }
					else
					{
					  env->ReleaseStringUTFChars( property, chars );
			          return false;
		            }
                    env->ReleaseStringUTFChars( property, chars );
                }
            }
	    }
    }
	return false;
}





/**
// this routine is not safe using current D3D routines
VOID D3dCtx::getDXVersion(CHAR* strResult)
{
	HRESULT hr;
  //  TCHAR strResult[128];

    DWORD dwDirectXVersion = 0;
    TCHAR strDirectXVersion[10];

    hr = GetDXVersion( &dwDirectXVersion, strDirectXVersion, 10 );
    if( SUCCEEDED(hr) )
    {
        strcpy( strResult, "DirectX ");
        if( dwDirectXVersion > 0 )

            strcpy( strResult, strDirectXVersion );
        else
            strcpy( strResult, "not installed") );
    }
    else
    {
      strcpy( strResult, "Unknown version of DirectX installed");
    }

}
	**/