/*
 * $RCSfile: Attributes.cpp,v $
 *
 * Copyright 2000-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 * $Revision: 1.21 $
 * $Date: 2008/02/28 20:17:58 $
 * $State: Exp $
 */

#include "StdAfx.h"


const DWORD blendFunctionTable[] =
{
    D3DBLEND_ZERO, D3DBLEND_ONE,
    D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA,
    D3DBLEND_DESTCOLOR, D3DBLEND_SRCCOLOR,
    D3DBLEND_INVSRCCOLOR, D3DBLEND_SRCCOLOR
};

const DWORD combineFunctionTable[] =
{
 D3DTOP_SELECTARG1,  D3DTOP_SELECTARG1,  D3DTOP_SELECTARG1,  D3DTOP_SELECTARG1,
 D3DTOP_MODULATE,    D3DTOP_MODULATE2X,  D3DTOP_MODULATE2X,  D3DTOP_MODULATE4X,
 D3DTOP_ADD,         D3DTOP_ADD,         D3DTOP_ADD,         D3DTOP_ADD,
 D3DTOP_ADDSIGNED,   D3DTOP_ADDSIGNED2X, D3DTOP_ADDSIGNED2X, D3DTOP_ADDSIGNED2X,
 D3DTOP_SUBTRACT,    D3DTOP_SUBTRACT,    D3DTOP_SUBTRACT,    D3DTOP_SUBTRACT,
 D3DTOP_LERP,        D3DTOP_LERP,        D3DTOP_LERP,        D3DTOP_LERP,
 D3DTOP_DOTPRODUCT3, D3DTOP_DOTPRODUCT3, D3DTOP_DOTPRODUCT3, D3DTOP_DOTPRODUCT3
};

// Assume COMBINE_OBJECT_COLOR	              = 0
//        COMBINE_TEXTURE_COLOR	              = 1
//        COMBINE_CONSTANT_COLOR              = 2
//        COMBINE_PREVIOUS_TEXTURE_UNIT_STATE = 3
//
//        COMBINE_SRC_COLOR		= 0
//        COMBINE_ONE_MINUS_SRC_COLOR	= 1
//        COMBINE_SRC_ALPHA		= 2
//        COMBINE_ONE_MINUS_SRC_ALPHA	= 3
//
const DWORD combineSourceTable[] =
{
   D3DTA_DIFFUSE,
   D3DTA_DIFFUSE | D3DTA_COMPLEMENT,
   D3DTA_DIFFUSE | D3DTA_ALPHAREPLICATE,
   D3DTA_DIFFUSE | D3DTA_COMPLEMENT | D3DTA_ALPHAREPLICATE,
   D3DTA_TEXTURE,
   D3DTA_TEXTURE | D3DTA_COMPLEMENT,
   D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE,
   D3DTA_TEXTURE | D3DTA_COMPLEMENT | D3DTA_ALPHAREPLICATE,
   D3DTA_TFACTOR,
   D3DTA_TFACTOR | D3DTA_COMPLEMENT,
   D3DTA_TFACTOR | D3DTA_ALPHAREPLICATE,
   D3DTA_TFACTOR | D3DTA_COMPLEMENT | D3DTA_ALPHAREPLICATE,
   D3DTA_CURRENT,
   D3DTA_CURRENT | D3DTA_COMPLEMENT,
   D3DTA_CURRENT | D3DTA_ALPHAREPLICATE,
   D3DTA_CURRENT | D3DTA_COMPLEMENT | D3DTA_ALPHAREPLICATE
};

// Assume  TEXTURE_COORDINATE_2 = 0
//         TEXTURE_COORDINATE_3 = 1;
//         TEXTURE_COORDINATE_4 = 2;
const int coordFormatTable[] = {2, 3, 4};

BOOL isLineWidthMessOutput = false;
BOOL isBackFaceMessOutput = false;
BOOL isLinePatternMessOutput = false;
BOOL isTexBorderMessOutput = false;

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateLinearFog(
	JNIEnv *env,
	jobject obj,
	jlong ctx,
	jfloat red,
	jfloat green,
	jfloat blue,
	jdouble fdist,
	jdouble bdist)
{
    GetDevice();

    float fstart = (float) fdist;
    float fend = (float) bdist;

    device->SetRenderState(d3dCtx->deviceInfo->fogMode,
			   D3DFOG_LINEAR);
    device->SetRenderState(D3DRS_FOGCOLOR,
			   D3DCOLOR_COLORVALUE(red, green, blue, 0));

    device->SetRenderState(D3DRS_FOGSTART,
			   *((LPDWORD) (&fstart)));
    device->SetRenderState(D3DRS_FOGEND,
			   *((LPDWORD) (&fend)));
    device->SetRenderState(D3DRS_FOGENABLE, TRUE);

}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateExponentialFog(
	JNIEnv *env,
	jobject obj,
	jlong  ctx,
	jfloat red,
	jfloat green,
	jfloat blue,
	jfloat density)
{
    GetDevice();

    float d = (float) density;

    device->SetRenderState(d3dCtx->deviceInfo->fogMode,
			   D3DFOG_EXP);
    device->SetRenderState(D3DRS_FOGCOLOR,
			   D3DCOLOR_COLORVALUE(red, green, blue, 0));
    device->SetRenderState(D3DRS_FOGDENSITY,
			   *((LPDWORD) (&d)));
    device->SetRenderState(D3DRS_FOGENABLE, TRUE);

}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateModelClip(
	JNIEnv *env,
	jobject obj,
	jlong ctx,
	jint planeNum,
	jboolean enableFlag,
	jdouble A,
	jdouble B,
	jdouble C,
	jdouble D)
{
    DWORD status;
    float clip[4];

    GetDevice();

    clip[0] = -A;
    clip[1] = -B;
    clip[2] = -C;
    clip[3] = -D;

    device->GetRenderState(D3DRS_CLIPPLANEENABLE, &status);

    if (enableFlag) {
	device->SetClipPlane(planeNum, clip);
	device->SetRenderState(D3DRS_CLIPPLANEENABLE,
			       status | (1 << planeNum));
    } else {
	device->SetRenderState(D3DRS_CLIPPLANEENABLE,
			       status & ~(1 << planeNum));
    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setModelViewMatrix(
    JNIEnv * env,
    jobject obj,
    jlong ctx,
    jdoubleArray viewMatrix,
    jdoubleArray modelMatrix)
{
    D3DXMATRIX d3dMatrix;

    GetDevice();


    jdouble *matrix = reinterpret_cast<jdouble*>(
	       env->GetPrimitiveArrayCritical(modelMatrix, NULL));

    CopyTranspose(d3dMatrix, matrix);

    env->ReleasePrimitiveArrayCritical(modelMatrix, matrix, 0);


    device->SetTransform(D3DTS_WORLD,&d3dMatrix);

    matrix = reinterpret_cast<jdouble*>(
		        env->GetPrimitiveArrayCritical(viewMatrix, NULL));
    CopyTranspose(d3dMatrix, matrix);

    env->ReleasePrimitiveArrayCritical(viewMatrix, matrix, 0);

    // Because we negate the third row in projection matrix to
    // make ._34 = 1. Here we negate the third column of View
    // matrix to compensate it.
    d3dMatrix._13 = -d3dMatrix._13;
    d3dMatrix._23 = -d3dMatrix._23;
    d3dMatrix._33 = -d3dMatrix._33;
    d3dMatrix._43 = -d3dMatrix._43;

    device->SetTransform(D3DTS_VIEW, &d3dMatrix);
}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setProjectionMatrix(
    JNIEnv * env,
    jobject obj,
    jlong ctx,
    jdoubleArray projMatrix)
{

    GetDevice();

    jdouble *s = reinterpret_cast<jdouble*>(
		    env->GetPrimitiveArrayCritical(projMatrix, NULL));

    /*
     * There are five steps we need to do in order that this
     * matrix is useful by D3D
     *
     * (1) We need to transpose the matrix since in Java3D v' = M*v
     *     but in Direct3D v' = v*N where N is the transpose of M
     *
     * (2) Invert the Z value in clipping coordinates because OpenGL
     *     uses left-handed clipping coordinates, while Java3D defines
     *     right-handed coordinates everywhere. i.e. do the following
     *     after the transpose
     *
     *    d3dMatrix._13 *= -1;
     *	  d3dMatrix._23 *= -1;
     *	  d3dMatrix._33 *= -1;
     *	  d3dMatrix._43 *= -1;
     *
     * (3) In Direct3D, the z-depths range is [0,1] instead of
     *     OGL [-1, 1], so we need to multiple it by
     *
     *	        [1 0   0 0]
     *      R = [0 1   0 0]
     *	        [0 0 0.5 0]
     *		[0 0 0.5 1]
     *
     *     after the transpose and negate. i.e. N*R
     *
     * (4) We want w-friendly perspective matrix, i.e., d3dMatrix._34 = 1
     *     We do this first by divide the whole matrix by
     *     1/d3dMatrix._34 Since d3dMatrix._34 is always negative as
     *     input from Java3D.  Now d3dMatrix._34 = -1
     *
     * (5) To make d3dMatrix._34 = 1, we negate the third row of it.
     *     Because of this, we need to negate the third column in
     *     View matrix to compensate this.
     *
     * All of the above operation is combined together in this
     * implementation for optimization.
     */
    D3DXMATRIX m;

    if (s[14] != 0) {
	// Perspective projection
	// s[14] is always < 0
	float ratio = -1/s[14];
	m._12= m._13 = m._14 = m._21 = m._23 =
	    m._24 = m._41 = m._42 = m._44 = 0;
	m._11 = s[0]*ratio;
	m._22 = s[5]*ratio;
	m._31 = -s[2]*ratio;
	m._32 = -s[6]*ratio;
	m._33 = -(s[14]-s[10])*ratio/2;
	m._43 = -s[11]*ratio/2;
	m._34 = 1;
    } else {
	// parallel projection
	m._12 = m._13 = m._14 = m._21 = m._23 =
	    m._24 = m._31 = m._32 = m._34 = 0;
	m._11 = s[0];
	m._22 = s[5];
	m._33 = s[10]/2;
	m._41 = s[3];
	m._42 = s[7];
	m._43 = (s[15]-s[11])/2;
	m._44 = s[15];
    }

    env->ReleasePrimitiveArrayCritical(projMatrix, s, 0);
    device->SetTransform(D3DTS_PROJECTION,  &m);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setViewport(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint x,
    jint y,
    jint width,
    jint height)
{
    GetDevice();

    if (d3dCtx->bFullScreen) {
	width = d3dCtx->devmode.dmPelsWidth;
	height = d3dCtx->devmode.dmPelsHeight;
    }
    D3DVIEWPORT9 vp = {x, y, width, height, 0.0f, 1.0f};

    device->SetViewport(&vp);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setSceneAmbient(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat red,
    jfloat green,
    jfloat blue)
{
    GetDevice();
    /*
    Clamp(red);
    Clamp(green);
    Clamp(blue);
    */
    device->SetRenderState(D3DRS_AMBIENT,
			   D3DCOLOR_COLORVALUE(red, green, blue, 0));
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setLightEnables(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jlong enable_mask,
    jint nlights)
{
    GetDevice();

#pragma warning(disable:4244)   // loss of data from __int64 to int

    for (int i=nlights-1; i>=0; i--) {
	device->LightEnable(i, enable_mask & (long)(1L<<i));
    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setLightingEnable(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean lightingOn)
{
    GetDevice();

    d3dCtx->isLightEnable = lightingOn;
    device->SetRenderState(D3DRS_LIGHTING, lightingOn);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_disableFog(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();
    device->SetRenderState(D3DRS_FOGENABLE, false);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_disableModelClip(
        JNIEnv *env,
        jobject obj,
	jlong ctx)
{
    GetDevice();
    device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
}

/**
 * one of :
 * STENCIL_KEEP - keeps the current value (no operation performed). This is the default setting.
 * STENCIL_ZERO - Sets the stencil buffer value to 0.
 * STENCIL_REPLACE - Sets the stencil buffer value to refValue, as specified by setStencilFunction.
 * STENCIL_INCR - Increments the current stencil buffer value.
 * STENCIL_DECR - Decrements the current stencil buffer value.
 * STENCIL_INVERT - Bitwise inverts the current stencil buffer value.
*/
DWORD getStencilOP(jint op)
{
	DWORD value = D3DSTENCILOP_KEEP;
	switch(op)
	{
	case javax_media_j3d_RenderingAttributes_STENCIL_KEEP:
		value = D3DSTENCILOP_KEEP; break;
	case javax_media_j3d_RenderingAttributes_STENCIL_ZERO:
		value = D3DSTENCILOP_ZERO; break;
	case javax_media_j3d_RenderingAttributes_STENCIL_REPLACE:
		value = D3DSTENCILOP_REPLACE; break;
	case javax_media_j3d_RenderingAttributes_STENCIL_INCR:
		value = D3DSTENCILOP_INCRSAT; break;
	case javax_media_j3d_RenderingAttributes_STENCIL_DECR:
		value = D3DSTENCILOP_DECRSAT; break;
	case javax_media_j3d_RenderingAttributes_STENCIL_INVERT:
		value = D3DSTENCILOP_INVERT; break;
	default :
		value = D3DSTENCILOP_KEEP; break;
	}

	return value;
}

/**
 * ALWAYS - pixels are always drawn, irrespective of the stencil value. This effectively disables stencil testing. This is the default setting.
 * NEVER - pixels are never drawn, irrespective of the stencil value.
 * EQUAL - pixels are drawn if the stencil reference value is equal to the stored stencil value in the frame buffer.
 * NOT_EQUAL - pixels are drawn if the stencil reference value is not equal to the stored stencil value in the frame buffer.
 * LESS - pixels are drawn if the stencil reference value is less than the stored stencil value in the frame buffer.
 * LESS_OR_EQUAL - pixels are drawn if the stencil reference value is less than or equal to the stored stencil value in the frame buffer.
 * GREATER - pixels are drawn if the stencil reference value is greater than the stored stencil value in the frame buffer.
 * GREATER_OR_EQUAL - pixels are drawn if the stencil reference value is greater than or equal to the stored stencil value in the frame buffer.
*/
DWORD getStencilFunc(jint func)
{
	DWORD value = D3DCMP_ALWAYS;
	switch(func)
	{
	case javax_media_j3d_RenderingAttributes_ALWAYS:
		value = D3DCMP_ALWAYS; break;
	case javax_media_j3d_RenderingAttributes_NEVER:
		value = D3DCMP_NEVER; break;
	case javax_media_j3d_RenderingAttributes_EQUAL:
		value = D3DCMP_EQUAL; break;
	case javax_media_j3d_RenderingAttributes_NOT_EQUAL:
		value = D3DCMP_NOTEQUAL; break;
	case javax_media_j3d_RenderingAttributes_LESS_OR_EQUAL:
		value = D3DCMP_LESSEQUAL; break;
	case javax_media_j3d_RenderingAttributes_GREATER:
		value = D3DCMP_GREATER; break;
	case javax_media_j3d_RenderingAttributes_GREATER_OR_EQUAL:
		value = D3DCMP_GREATEREQUAL; break;
	default :
		value = D3DCMP_ALWAYS; break;
	}

	return value;
}

/**
 * LESS_OR_EQUAL - DEFAULT pixels are drawn if the depth value is less than or equal to the stored depth value in the frame buffer.
 * ALWAYS - pixels are always drawn, irrespective of the depth value. This effectively disables depth testing.
 * NEVER - pixels are never drawn, irrespective of the depth value.
 * EQUAL - pixels are drawn if the depth value is equal to the stored stencil value in the frame buffer.
 * NOT_EQUAL - pixels are drawn if the depth value is not equal to the stored depth value in the frame buffer.
 * LESS - pixels are drawn if the depth value is less than the stored stencil value in the frame buffer.
 * GREATER - pixels are drawn if the depth value is greater than the stored stencil value in the frame buffer.
 * GREATER_OR_EQUAL - pixels are drawn if the depth value is greater than or equal to the stored stencil value in the frame buffer.
*/
DWORD getDepthFunc(jint func)
{
	DWORD value = D3DCMP_LESSEQUAL;
	switch(func)
	{
	case javax_media_j3d_RenderingAttributes_ALWAYS:
		value = D3DCMP_ALWAYS; break;
	case javax_media_j3d_RenderingAttributes_NEVER:
		value = D3DCMP_NEVER; break;
	case javax_media_j3d_RenderingAttributes_EQUAL:
		value = D3DCMP_EQUAL; break;
	case javax_media_j3d_RenderingAttributes_NOT_EQUAL:
		value = D3DCMP_NOTEQUAL; break;
	case javax_media_j3d_RenderingAttributes_LESS_OR_EQUAL:
		value = D3DCMP_LESSEQUAL; break;
	case javax_media_j3d_RenderingAttributes_GREATER:
		value = D3DCMP_GREATER; break;
	case javax_media_j3d_RenderingAttributes_GREATER_OR_EQUAL:
		value = D3DCMP_GREATEREQUAL; break;
	default :
		value = D3DCMP_LESSEQUAL; break;
	}

	return value;
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetRenderingAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean db_write_enable_override,
    jboolean db_enable_override)
{
    GetDevice();

    if (!db_write_enable_override) {
	d3dCtx->zWriteEnable = TRUE;
	device->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_TRUE);
	device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
    }
    
    if (!db_enable_override) {
	d3dCtx->zEnable = TRUE;
	device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
    }
    
    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
    device->SetRenderState(D3DRS_ALPHAREF, 0);
    if(d3dCtx->stencilWriteEnable) {
	device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
	device->SetRenderState( D3DRS_STENCILREF,  0);
	device->SetRenderState( D3DRS_STENCILMASK,      0xFFFFFFFF);
	device->SetRenderState( D3DRS_STENCILWRITEMASK, 0xFFFFFFFF );
	
	// Always increment the stencil value
	device->SetRenderState(D3DRS_STENCILFAIL,  D3DSTENCILOP_KEEP);
        device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
        device->SetRenderState(D3DRS_STENCILPASS,  D3DSTENCILOP_KEEP);
	
	device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
	d3dCtx->stencilWriteEnable = false;
    }
    
    /*    setRasterOp(d3dCtx, R2_COPYPEN); */

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateRenderingAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean db_write_enable_override,
    jboolean db_enable_override,
    jboolean db_enable,
    jboolean db_write_enable,
    jint db_func,
    jfloat at_value,
    jint at_func,
    jboolean ignoreVertexColors,
    jboolean rasterOpEnable,
    jint rasterOp,
    jboolean userStencilAvailable,
    jboolean stencilEnable,
    jint stencilFailOp,
    jint stencilZFailOp,
    jint stencilZPassOp,
    jint stencilFunction,
    jint stencilReferenceValue,
    jint stencilCompareMask,
    jint stencilWriteMask)
{

    GetDevice();

    DWORD alpha = (DWORD) (at_value * 255 + 0.5f);

    if (!db_enable_override)
	{
        if (db_enable)
		{
	      d3dCtx->zEnable = TRUE;
	      device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
        }
		else
		{
	      d3dCtx->zEnable = FALSE;
	      device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);		  
        }
    }

    if (!db_write_enable_override)
	{
	  d3dCtx->zWriteEnable = db_write_enable;
	  device->SetRenderState(D3DRS_ZWRITEENABLE, db_write_enable);
	  // disable ZFunc if ZBuffer is disabled ? no. 
	  //  ZFunc must work even when there is no z-buffer enable
      device->SetRenderState(D3DRS_ZFUNC, getDepthFunc(db_func));	  
    }

    if (at_func == javax_media_j3d_RenderingAttributes_ALWAYS)
	{
	  device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    }
	else
	{
	  device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
	  device->SetRenderState(D3DRS_ALPHAREF, alpha);
    }

    switch (at_func)
	{
	case javax_media_j3d_RenderingAttributes_ALWAYS:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
	    break;
	case javax_media_j3d_RenderingAttributes_NEVER:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NEVER);
	    break;
	case javax_media_j3d_RenderingAttributes_EQUAL:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_EQUAL);
	    break;
	case javax_media_j3d_RenderingAttributes_NOT_EQUAL:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL);
	    break;
	case javax_media_j3d_RenderingAttributes_LESS:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS);
	    break;
	case javax_media_j3d_RenderingAttributes_LESS_OR_EQUAL:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESSEQUAL);
	    break;
	case javax_media_j3d_RenderingAttributes_GREATER:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
	    break;
	case javax_media_j3d_RenderingAttributes_GREATER_OR_EQUAL:
	    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
	    break;
    }

/*
	 stencilFailOp,  *
     stencilZFailOp, *
     stencilZPassOp, *
     stencilFunction, *
     stencilReferenceValue, *
     stencilCompareMask, *
     stencilWriteMask *
*/
	if (userStencilAvailable == JNI_TRUE)
	{
        if (stencilEnable == JNI_TRUE)
		{
         // Turn on stenciling
         device->SetRenderState( D3DRS_STENCILENABLE, TRUE );
        // printf("StencilEnable TRUE\n");
         // Set the function to always pass.
         device->SetRenderState( D3DRS_STENCILFUNC, getStencilFunc(stencilFunction) );
         device->SetRenderState( D3DRS_STENCILREF,  stencilReferenceValue );
		 device->SetRenderState( D3DRS_STENCILMASK, stencilCompareMask );
		 device->SetRenderState( D3DRS_STENCILWRITEMASK, stencilWriteMask );

         // Always increment the stencil value
		 device->SetRenderState(D3DRS_STENCILFAIL,  getStencilOP(stencilFailOp) );
         device->SetRenderState(D3DRS_STENCILZFAIL, getStencilOP(stencilZFailOp));
         device->SetRenderState(D3DRS_STENCILPASS,  getStencilOP(stencilZPassOp) );
   	   }
	   else
	   {
		 device->SetRenderState( D3DRS_STENCILENABLE, FALSE );
		 // printf("StencilEnable False\n");
       }
    }
	/*
	else
	{
		 printf("UserStencilEnable False\n");
	}
	*/

}//



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetPolygonAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();
    // D3D vertex order is reverse of OGL
    d3dCtx->cullMode = D3DCULL_CW;
    d3dCtx->fillMode = D3DFILL_SOLID;
    device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
    d3dCtx->twoSideLightingEnable = false;
    device->SetRenderState(D3DRS_DEPTHBIAS, 0);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updatePolygonAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint polygonMode,
    jint cullFace,
    jboolean backFaceNormalFlip,
    jfloat polygonOffset,
    jfloat polygonOffsetFactor)
{

    GetDevice();

    jfloat zbias = polygonOffset + polygonOffsetFactor;
    DWORD zbias_w = 0;
    /*
     * DirectX support Z-bias from 0 to 16 only and the
     * direction is opposite to OGL. If we pass negative
     * Z-bias the polygon will not render at all.
     * So we map -ve polygon offset to positive value
     * and +ve offset to 0. (i.e. we don't support positive
     * polygon offset)
     */
    if (zbias <= -1) {
	zbias_w = max(-zbias/50, 1);

	if (zbias_w > 16) {
	    zbias_w = 16;
	}
    }

    device->SetRenderState(D3DRS_DEPTHBIAS, zbias_w);

    if (cullFace == javax_media_j3d_PolygonAttributes_CULL_NONE) {
	d3dCtx->cullMode = D3DCULL_NONE;
	device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    } else {
        if (cullFace == javax_media_j3d_PolygonAttributes_CULL_BACK) {
	    d3dCtx->cullMode = D3DCULL_CW;
	    device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
        } else {
	    d3dCtx->cullMode = D3DCULL_CCW;
	    device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
	}
    }

    if (polygonMode == javax_media_j3d_PolygonAttributes_POLYGON_POINT) {
	d3dCtx->fillMode = D3DFILL_POINT;
	device->SetRenderState(D3DRS_FILLMODE, D3DFILL_POINT);
    } else if (polygonMode == javax_media_j3d_PolygonAttributes_POLYGON_LINE) {
	d3dCtx->fillMode = D3DFILL_WIREFRAME;
	device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
    } else {
	d3dCtx->fillMode = D3DFILL_SOLID;
	device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    }

    /*
    if (debug && !isBackFaceMessOutput &&
	(backFaceNormalFlip) && (cullFace != javax_media_j3d_PolygonAttributes_CULL_BACK)) {
	isBackFaceMessOutput = true;
	printf("BackFaceNormalFlip is not support !\n");
    }
    */
}

/*
void printDepthFunc(jint func)
{
	DWORD value = D3DCMP_LESSEQUAL;
	printf("DepthFunc: ");
	switch(func)
	{
	case javax_media_j3d_RenderingAttributes_ALWAYS:
		printf(" D3DCMP_ALWAYS\n"); break;
	case javax_media_j3d_RenderingAttributes_NEVER:
		printf(" D3DCMP_NEVER\n"); break;
	case javax_media_j3d_RenderingAttributes_EQUAL:
		printf(" D3DCMP_EQUAL\n"); break;
	case javax_media_j3d_RenderingAttributes_NOT_EQUAL:
		printf(" D3DCMP_NOTEQUAL\n"); break;
	case javax_media_j3d_RenderingAttributes_LESS_OR_EQUAL:
		printf(" D3DCMP_LESSEQUAL\n"); break;
	case javax_media_j3d_RenderingAttributes_GREATER:
		printf(" D3DCMP_GREATER\n"); break;
	case javax_media_j3d_RenderingAttributes_GREATER_OR_EQUAL:
		printf(" D3DCMP_GREATEREQUAL\n"); break;
	default :
		printf(" D3DCMP_LESSEQUAL\n"); break;
	}
}

*/

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetLineAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    // GetDevice();

    // D3D don't support Line width
    // glLineWidth(1);
    // D3D9 doesnot support line Patterns
    // @TODO must update this to use ID3DXLine Interface
    /*
      D3DLINEPATTERN pattern;
      pattern.wRepeatFactor = 0;
      pattern.wLinePattern = 0;
      device->SetRenderState(D3DRS_LINEPATTERN,
      *((LPDWORD) (&pattern)));
    */
}


// Note that some graphics card don't support it.
// In this case use RGB Emulation.
extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateLineAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat lineWidth,
    jint linePattern,
    jint linePatternMask,
    jint linePatternScaleFactor,
    jboolean lineAntialiasing)
{
    // GetDevice();
    
    //Alessandro
    //D3DLINEPATTERN pattern;

    /*
    if (lineWidth > 1) {
	if (debug && !isLineWidthMessOutput) {
	    isLineWidthMessOutput = true;
	    printf("Line width > 1 not support !\n");
	}
    }
    */
    // glLineWidth(lineWidth);
  /** Alessandro
    if (linePattern == javax_media_j3d_LineAttributes_PATTERN_SOLID) {
	pattern.wRepeatFactor = 0;
	pattern.wLinePattern = 0;

    } else {
	**/

	/*
	if (!d3dCtx->deviceInfo->linePatternSupport) {
	    if (debug && !isLinePatternMessOutput) {
		printf("Device not support line pattern !\n");
		isLinePatternMessOutput = false;
	    }
	}
	*/
	/** alessandro
        if (linePattern == javax_media_j3d_LineAttributes_PATTERN_DASH) { // dashed lines
	    pattern.wRepeatFactor = 1;
	    pattern.wLinePattern = 0x00ff;
        } else if (linePattern == javax_media_j3d_LineAttributes_PATTERN_DOT) { // dotted lines
	    pattern.wRepeatFactor = 1;
	    pattern.wLinePattern = 0x0101;
        } else if (linePattern == javax_media_j3d_LineAttributes_PATTERN_DASH_DOT) { // dash-dotted lines
	    pattern.wRepeatFactor = 1;
	    pattern.wLinePattern = 0x087f;
	} else if (linePattern == javax_media_j3d_LineAttributes_PATTERN_USER_DEFINED) { // user-defined mask
	    pattern.wRepeatFactor = linePatternScaleFactor;
	    pattern.wLinePattern = (WORD) linePatternMask;
	}
    }

    device->SetRenderState(D3DRS_LINEPATTERN,
			   *((LPDWORD) (&pattern)));
      **/
    /*
      if (lineAntialiasing == JNI_TRUE) {
      glEnable (GL_LINE_SMOOTH);
      } else {
      glDisable (GL_LINE_SMOOTH);
      }
    */
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetPointAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();

    if (d3dCtx->pointSize != 1.0f) {
	d3dCtx->pointSize = 1.0f;
	device->SetRenderState(D3DRS_POINTSIZE, *((LPDWORD) &d3dCtx->pointSize));
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updatePointAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat pointSize,
    jboolean pointAntialiasing)
{
    // point Antialiasing not support by D3D
    GetDevice();

    if (pointSize < 1.0f) {
	// We don't want to set pointSize unnecessary and
	// trigger the software vertex processing mode in
	// D3DVertexBuffer if possible. It is an error
	// to set pointSize to zero under OGL.
	pointSize = 1.0f;
    }

    if (d3dCtx->pointSize != pointSize) {
	device->SetRenderState(D3DRS_POINTSIZE, *((LPDWORD)
						  &pointSize));
	d3dCtx->pointSize = pointSize;
    }
}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetTexCoordGeneration(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();

    int tus = d3dCtx->texUnitStage;

    if (tus >= d3dCtx->bindTextureIdLen) {
	return;
    }

    d3dCtx->texGenMode[tus] = TEX_GEN_NONE;

    if (d3dCtx->texTransformSet[tus]) {
	device->SetTransform((D3DTRANSFORMSTATETYPE)
			     (D3DTS_TEXTURE0 + tus),
			     &(d3dCtx->texTransform[tus]));
    }

    device->SetTextureStageState(tus,
				 D3DTSS_TEXCOORDINDEX,
				 D3DTSS_TCI_PASSTHRU);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexCoordGeneration(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean enable,
    jint genMode,
    jint format,
    jfloat planeSx,
    jfloat planeSy,
    jfloat planeSz,
    jfloat planeSw,
    jfloat planeTx,
    jfloat planeTy,
    jfloat planeTz,
    jfloat planeTw,
    jfloat planeRx,
    jfloat planeRy,
    jfloat planeRz,
    jfloat planeRw,
    jfloat planeQx,
    jfloat planeQy,
    jfloat planeQz,
    jfloat planeQw,
    jdoubleArray eyeToVworld)
{

    D3DXMATRIX m;
    jdouble *mv;
    GetDevice();

    int tus = d3dCtx->texUnitStage;


    if (tus >= d3dCtx->bindTextureIdLen) {
	return;
    }

    if (!enable) {
	device->SetTextureStageState(tus,
				     D3DTSS_TEXCOORDINDEX,
				     D3DTSS_TCI_PASSTHRU);
	d3dCtx->texGenMode[tus] = TEX_GEN_NONE;
	return;
    }

    d3dCtx->texCoordFormat[tus] = coordFormatTable[format];

//  printf("TexCoordGenerationRetained_updateNative texStage %d set Mode %d, format %d, texTransformSet %d\n", tus, genMode, format, d3dCtx->texTransformSet[tus]);

    switch (genMode) {
    case javax_media_j3d_TexCoordGeneration_EYE_LINEAR:
	// Generated Coordinate = p1'*x + p2'*y + p3'*z + p4'*w;
	// where (p1', p2', p3', p4') = (p1 p2 p3 p4)*eyeToVworld
	mv = (jdouble * ) env->GetPrimitiveArrayCritical(eyeToVworld, 0);
	m._11 = planeSx*mv[0] + planeSy*mv[4] + planeSz*mv[8]  + planeSw*mv[12];
	m._21 = planeSx*mv[1] + planeSy*mv[5] + planeSz*mv[9]  + planeSw*mv[13];
	m._31 = planeSx*mv[2] + planeSy*mv[6] + planeSz*mv[10] + planeSw*mv[14];
	m._41 = planeSx*mv[3] + planeSy*mv[7] + planeSz*mv[11] + planeSw*mv[15];
	m._12 = planeTx*mv[0] + planeTy*mv[4] + planeTz*mv[8]  + planeTw*mv[12];
	m._22 = planeTx*mv[1] + planeTy*mv[5] + planeTz*mv[9]  + planeTw*mv[13];
	m._32 = planeTx*mv[2] + planeTy*mv[6] + planeTz*mv[10] + planeTw*mv[14];
	m._42 = planeTx*mv[3] + planeTy*mv[7] + planeTz*mv[11] + planeTw*mv[15];


	if (format >= javax_media_j3d_TexCoordGeneration_TEXTURE_COORDINATE_3) {
	    m._13 = planeRx*mv[0] + planeRy*mv[4] + planeRz*mv[8]  + planeRw*mv[12];
	    m._23 = planeRx*mv[1] + planeRy*mv[5] + planeRz*mv[9]  + planeRw*mv[13];
	    m._33 = planeRx*mv[2] + planeRy*mv[6] + planeRz*mv[10] + planeRw*mv[14];
	    m._43 = planeRx*mv[3] + planeRy*mv[7] + planeRz*mv[11] + planeRw*mv[15];

	    if (format >= javax_media_j3d_TexCoordGeneration_TEXTURE_COORDINATE_4) {
		m._14 = planeQx*mv[0] + planeQy*mv[4] + planeQz*mv[8]  + planeQw*mv[12];
		m._24 = planeQx*mv[1] + planeQy*mv[5] + planeQz*mv[9]  + planeQw*mv[13];
		m._34 = planeQx*mv[2] + planeQy*mv[6] + planeQz*mv[10] + planeQw*mv[14];
		m._44 = planeQx*mv[3] + planeQy*mv[7] + planeQz*mv[11] + planeQw*mv[15];
	    } else {
		m._14 = 0;
		m._24 = 0;
		m._34 = 0;
		m._44 = 0;
	    }
	} else {
	    m._13 = 0;
	    m._23 = 0;
	    m._33 = 0;
	    m._43 = 0;
	    m._14 = 0;
	    m._24 = 0;
	    m._34 = 0;
	    m._44 = 0;
	}

	env->ReleasePrimitiveArrayCritical(eyeToVworld, mv, 0);

	if (d3dCtx->texTransformSet[tus]) {
	    device->MultiplyTransform((D3DTRANSFORMSTATETYPE)
				      (D3DTS_TEXTURE0 + tus) , &m);
	} else {
	    device->SetTransform((D3DTRANSFORMSTATETYPE)
				 (D3DTS_TEXTURE0 + tus), &m);
	    d3dCtx->texTransformSet[tus] = true;
	}
	d3dCtx->texGenMode[tus] = TEX_EYE_LINEAR;

	break;

    case javax_media_j3d_TexCoordGeneration_SPHERE_MAP:
	/*
	  The matrix has to scale and translate the texture coordinates
	  Since in sphere map Tx = Nx/2 + 0.5, Ty = Ny/2 + 0.5
	*/
	m._11 = 0.5f; m._12 = 0.0f; m._13 = 0.0f; m._14 = 0.0f;
	m._21 = 0.0f; m._22 = 0.5f; m._23 = 0.0f; m._24 = 0.0f;
	m._31 = 0.0f; m._32 = 0.0f; m._33 = 1.0f; m._34 = 0.0f;
	m._41 = 0.5f; m._42 = 0.5f; m._43 = 0.0f; m._44 = 1.0f;

	if (d3dCtx->texTransformSet[tus]) {
	    // If texture transform already set, multiple by this
	    // matrix.
	    device->MultiplyTransform((D3DTRANSFORMSTATETYPE)
				      (D3DTS_TEXTURE0 + tus) , &m);
	} else {
	    device->SetTransform((D3DTRANSFORMSTATETYPE)
				 (D3DTS_TEXTURE0 + tus), &m);
	    d3dCtx->texTransformSet[tus] = true;
	}

	d3dCtx->texGenMode[tus] = TEX_SPHERE_MAP;
	break;
    case javax_media_j3d_TexCoordGeneration_OBJECT_LINEAR:
	// OBJECT_LINEAR not support by D3D, we'll do it ourselve.
	d3dCtx->planeS[tus][0] = planeSx;
	d3dCtx->planeS[tus][1] = planeSy;
	d3dCtx->planeS[tus][2] = planeSz;
	d3dCtx->planeS[tus][3] = planeSw;
	d3dCtx->planeT[tus][0] = planeTx;
	d3dCtx->planeT[tus][1] = planeTy;
	d3dCtx->planeT[tus][2] = planeTz;
	d3dCtx->planeT[tus][3] = planeTw;
	d3dCtx->planeR[tus][0] = planeRx;
	d3dCtx->planeR[tus][1] = planeRy;
	d3dCtx->planeR[tus][2] = planeRz;
	d3dCtx->planeR[tus][3] = planeRw;
	d3dCtx->planeQ[tus][0] = planeQx;
	d3dCtx->planeQ[tus][1] = planeQy;
	d3dCtx->planeQ[tus][2] = planeQz;
	d3dCtx->planeQ[tus][3] = planeQw;
	d3dCtx->texGenMode[tus] = TEX_OBJ_LINEAR;
	break;
    case javax_media_j3d_TexCoordGeneration_NORMAL_MAP:
	d3dCtx->texGenMode[tus] = TEX_NORMAL_MAP;
	break;
    case javax_media_j3d_TexCoordGeneration_REFLECTION_MAP:
	d3dCtx->texGenMode[tus] = TEX_REFLECT_MAP;
	break;
    default:
	printf("Unknown TexCoordinate Generation mode %d\n", genMode);
    }
}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetTextureAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();

    int tus = d3dCtx->texUnitStage;

    if (tus >= d3dCtx->bindTextureIdLen) {
	return;
    }

    if (d3dCtx->texTransformSet[tus]) {
	d3dCtx->texTransformSet[tus] = false;
	device->SetTransform((D3DTRANSFORMSTATETYPE)
			     (D3DTS_TEXTURE0 + tus),
			     &identityMatrix);
    }

    // perspCorrectionMode always turn on in DX8.0 if device support

    device->SetTextureStageState(tus,
				 D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    device->SetTextureStageState(tus,
				 D3DTSS_COLORARG1, D3DTA_TEXTURE);
    device->SetTextureStageState(tus,
				 D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    device->SetTextureStageState(tus,
				 D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jdoubleArray transform, jboolean isIdentityT,
    jint textureMode,
    jint perspCorrectionMode,
    jfloat textureBlendColorRed,
    jfloat textureBlendColorGreen,
    jfloat textureBlendColorBlue,
    jfloat textureBlendColorAlpha,
    jint format)
{

    D3DCOLOR textureBlendColor;
    BOOL alphaDisable = FALSE;

    GetDevice();

    int tus = d3dCtx->texUnitStage;

    if (tus >= d3dCtx->bindTextureIdLen) {
	return;
    }

    // perspCorrectionMode always turn on in DX8.0 if device support

    if (isIdentityT) {
	d3dCtx->texTransformSet[tus] = false;
	device->SetTransform((D3DTRANSFORMSTATETYPE)
			     (D3DTS_TEXTURE0 + tus),
			     &identityMatrix);
    } else {
	D3DXMATRIX *m = &(d3dCtx->texTransform[tus]);
	jdouble *mx_ptr = reinterpret_cast<jdouble *>(
			     env->GetPrimitiveArrayCritical(transform, NULL));
	CopyTranspose((*m), mx_ptr);

	env->ReleasePrimitiveArrayCritical(transform, mx_ptr, 0);
	/*
	printf("set Tex Transform \n");
	printf("%f, %f, %f, %f\n", (*m)._11, (*m)._12, (*m)._13, (*m)._14);
	printf("%f, %f, %f, %f\n", (*m)._21, (*m)._22, (*m)._23, (*m)._24);
	printf("%f, %f, %f, %f\n", (*m)._31, (*m)._23, (*m)._33, (*m)._34);
	printf("%f, %f, %f, %f\n", (*m)._41, (*m)._42, (*m)._43, (*m)._44);
	*/
	d3dCtx->texTransformSet[tus] = true;
	d3dCtx->texTranslateSet[tus] = false;
	device->SetTransform((D3DTRANSFORMSTATETYPE)
			     (D3DTS_TEXTURE0 + tus), m);
    }

    /* set texture environment mode */

    switch (textureMode) {
        case javax_media_j3d_TextureAttributes_MODULATE:
	    switch (format) {
	    case J3D_RGBA:
	    case INTENSITY:
	    case LUMINANCE_ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_MODULATE);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG2, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG2, D3DTA_CURRENT);
		break;
	    case J3D_RGB:
	    case LUMINANCE:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_MODULATE);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG2, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		break;
	    case ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG2, D3DTA_CURRENT);
		break;
	    default:
		printf("Format %d not support\n", format);
	    }
	    break;
        case javax_media_j3d_TextureAttributes_DECAL:
	    switch (format) {
            case J3D_RGBA:
	    case INTENSITY:
	    case LUMINANCE_ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG2, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		break;
	    case J3D_RGB:
	    case LUMINANCE:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		break;
	    case ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		break;
	    default:
		printf("Format %d not support\n", format);
	    }
	    break;
        case javax_media_j3d_TextureAttributes_BLEND:
	    // Two pass is needed for this mode, the first pass
	    // will

	    textureBlendColor = D3DCOLOR_COLORVALUE(textureBlendColorRed,
						    textureBlendColorGreen,
						    textureBlendColorBlue,
						    textureBlendColorAlpha);

	    device->SetRenderState(D3DRS_TEXTUREFACTOR,
				   *((LPDWORD) &textureBlendColor));

	    switch (format) {
	    case ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG2, D3DTA_CURRENT);
		break;
	    case INTENSITY:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG2, D3DTA_TFACTOR);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_BLENDTEXTUREALPHA);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
		break;
	    case J3D_RGB:
	    case LUMINANCE:
		device->SetTextureStageState(0,
					     D3DTSS_ALPHAOP,
					     D3DTOP_DISABLE);
		alphaDisable = TRUE;
		// fallthrough
	    case J3D_RGBA:
	    case LUMINANCE_ALPHA:

		if (!d3dCtx->deviceInfo->texLerpSupport) {
		    // Use two pass, first pass will enable specular and
		    // compute Cf*(1 - Ct), second pass will disable specular and
		    // comptue  Cc*Ct. Note that multi-texturing is disable
		    // in this case, so stage 0 is always use.
		    device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		    device->SetTextureStageState(0, D3DTSS_COLORARG1,
						 D3DTA_TEXTURE|D3DTA_COMPLEMENT);
		    device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);

		    if (!alphaDisable) {
			device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
			device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
			device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
		    }
		} else {
		    device->SetTextureStageState(tus, D3DTSS_COLOROP, D3DTOP_LERP);
		    device->SetTextureStageState(tus, D3DTSS_COLORARG0, D3DTA_TEXTURE);
		    device->SetTextureStageState(tus, D3DTSS_COLORARG1, D3DTA_TFACTOR);
		    device->SetTextureStageState(tus, D3DTSS_COLORARG2, D3DTA_CURRENT);

		    if (!alphaDisable) {
			device->SetTextureStageState(tus, D3DTSS_ALPHAOP, D3DTOP_LERP);
			device->SetTextureStageState(tus, D3DTSS_ALPHAARG0, D3DTA_TEXTURE);
			device->SetTextureStageState(tus, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
			device->SetTextureStageState(tus, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
		    }
		}
		break;
	    default:
		printf("Format %d not support\n", format);
	    }


            break;
        case javax_media_j3d_TextureAttributes_REPLACE:
	    switch (format) {
	    case J3D_RGBA:
	    case INTENSITY:
	    case LUMINANCE_ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		break;
	    case J3D_RGB:
	    case LUMINANCE:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_TEXTURE);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		break;
	    case ALPHA:
		device->SetTextureStageState(tus,
					     D3DTSS_COLOROP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_COLORARG1, D3DTA_CURRENT);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		device->SetTextureStageState(tus,
					     D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		break;
	    default:
		printf("Format %d not support\n", format);
	    }
	    break;
      default:
	   // TEXTURE COMBINER case
	  break;
    }
}


// // This procedure is invoked after Blend2Pass to restore the original value
// extern "C" JNIEXPORT
// void JNICALL Java_javax_media_j3d_NativePipeline_restoreBlend1Pass(
//     JNIEnv *env,
//     jobject obj,
//     jlong ctx)
// {
//     GetDevice();

//     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
//     device->SetTextureStageState(0, D3DTSS_COLORARG1,
// 				 D3DTA_TEXTURE|D3DTA_COMPLEMENT);
//     device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);

//     device->SetRenderState(D3DRS_SRCBLEND,
// 			   d3dCtx->srcBlendFunc);
//     device->SetRenderState(D3DRS_DESTBLEND,
// 			   d3dCtx->dstBlendFunc);
//     device->SetRenderState(D3DRS_ALPHABLENDENABLE,
// 			   d3dCtx->blendEnable);
// }


// extern "C" JNIEXPORT
// void JNICALL Java_javax_media_j3d_NativePipeline_updateBlend2Pass(
//     JNIEnv *env,
//     jobject obj,
//     jlong ctx)
// {
//     GetDevice();
//     device->GetRenderState(D3DRS_SRCBLEND,
// 			   &d3dCtx->srcBlendFunc);
//     device->GetRenderState(D3DRS_DESTBLEND,
// 			   &d3dCtx->dstBlendFunc);
//     device->GetRenderState(D3DRS_ALPHABLENDENABLE,
// 			   &d3dCtx->blendEnable);

//     device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//     device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
//     device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
//     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
//     device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
//     device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
// }


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateCombiner(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint combineRgbMode,
    jint combineAlphaMode,
    jintArray combineRgbSrc,
    jintArray combineAlphaSrc,
    jintArray combineRgbFcn,
    jintArray combineAlphaFcn,
    jint combineRgbScale,
    jint combineAlphaScale)
{

    GetDevice();

    DWORD ts = d3dCtx->texUnitStage;

    jint *rgbSrc = (jint *) env->GetPrimitiveArrayCritical(combineRgbSrc, NULL);
    jint *alphaSrc = (jint *) env->GetPrimitiveArrayCritical(combineAlphaSrc, NULL);
    jint *rgbFcn = (jint *) env->GetPrimitiveArrayCritical(combineRgbFcn, NULL);
    jint *alphaFcn = (jint *) env->GetPrimitiveArrayCritical(combineAlphaFcn, NULL);


    device->SetTextureStageState(ts, D3DTSS_COLOROP,
				 combineFunctionTable[(combineRgbMode << 2) + combineRgbScale - 1]);

    if (combineRgbMode !=  javax_media_j3d_TextureAttributes_COMBINE_INTERPOLATE) {
	device->SetTextureStageState(ts, D3DTSS_COLORARG1,
				     combineSourceTable[(rgbSrc[0] << 2) + rgbFcn[0]]);
	if (combineRgbMode != javax_media_j3d_TextureAttributes_COMBINE_REPLACE) {
	    device->SetTextureStageState(ts, D3DTSS_COLORARG2,
					 combineSourceTable[(rgbSrc[1] << 2) + rgbFcn[1]]);
	}
    } else {
	device->SetTextureStageState(ts, D3DTSS_COLORARG1,
				     combineSourceTable[(rgbSrc[2] << 2) + rgbFcn[2]]);
	device->SetTextureStageState(ts, D3DTSS_COLORARG2,
				     combineSourceTable[(rgbSrc[0] << 2) + rgbFcn[0]]);
	device->SetTextureStageState(ts, D3DTSS_COLORARG0,
				     combineSourceTable[(rgbSrc[1] << 2) + rgbFcn[1]]);
    }

    device->SetTextureStageState(ts, D3DTSS_ALPHAOP,
				 combineFunctionTable[(combineAlphaMode << 2) + combineAlphaScale - 1]);

    if (combineAlphaMode != javax_media_j3d_TextureAttributes_COMBINE_INTERPOLATE) {
	device->SetTextureStageState(ts, D3DTSS_ALPHAARG1,
				     combineSourceTable[(alphaSrc[0] << 2) + alphaFcn[0]]);
	if (combineAlphaMode != javax_media_j3d_TextureAttributes_COMBINE_REPLACE) {
	    device->SetTextureStageState(ts, D3DTSS_ALPHAARG2,
					 combineSourceTable[(alphaSrc[1] << 2) + alphaFcn[1]]);
	}
    } else {
	device->SetTextureStageState(ts, D3DTSS_ALPHAARG0,
				     combineSourceTable[(alphaSrc[2] << 2) + alphaFcn[2]]);
	device->SetTextureStageState(ts, D3DTSS_ALPHAARG1,
				     combineSourceTable[(alphaSrc[0] << 2) + alphaFcn[0]]);
	device->SetTextureStageState(ts, D3DTSS_ALPHAARG2,
				     combineSourceTable[(alphaSrc[1] << 2) + alphaFcn[1]]);
    }

    env->ReleasePrimitiveArrayCritical(combineRgbSrc, rgbSrc, 0);
    env->ReleasePrimitiveArrayCritical(combineAlphaSrc, alphaSrc, 0);
    env->ReleasePrimitiveArrayCritical(combineRgbFcn, rgbFcn, 0);
    env->ReleasePrimitiveArrayCritical(combineAlphaFcn, alphaFcn, 0);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureColorTable(
	JNIEnv *env,
	jobject obj,
	jlong ctx,
	jint numComponents,
	jint colorTableSize,
	jintArray textureColorTable)
{
    // Not support by D3D
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateMaterialColor(
	JNIEnv *env,
	jobject obj,
	jlong ctx,
	jfloat colorRed,
	jfloat colorGreen,
	jfloat colorBlue,
	jfloat transparency)
{
    GetDevice();

    d3dCtx->currentColor_r = colorRed;
    d3dCtx->currentColor_g = colorGreen;
    d3dCtx->currentColor_b = colorBlue;
    d3dCtx->currentColor_a = transparency;

    d3dCtx->isLightEnable = false;
    device->SetRenderState(D3DRS_LIGHTING, false);
    if (d3dCtx->resetColorTarget) {
	device->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE,
			       D3DMCS_COLOR1);
	device->SetRenderState(D3DRS_SPECULARMATERIALSOURCE,
			       D3DMCS_MATERIAL);
	device->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE,
			       D3DMCS_MATERIAL);
	device->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE,
			       D3DMCS_MATERIAL);
	d3dCtx->resetColorTarget = false;
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateMaterial(
        JNIEnv *env,
        jobject obj,
	jlong ctx,
        jfloat colorRed,
        jfloat colorGreen,
        jfloat colorBlue,
        jfloat transparency,
        jfloat aRed,
        jfloat aGreen,
        jfloat aBlue,
        jfloat eRed,
        jfloat eGreen,
        jfloat eBlue,
        jfloat dRed,
        jfloat dGreen,
        jfloat dBlue,
        jfloat sRed,
        jfloat sGreen,
        jfloat sBlue,
        jfloat shininess,
	jint colorTarget,
        jboolean lightEnable)
{
    D3DMATERIAL9 material;

    GetDevice();

    switch (colorTarget) {
    case javax_media_j3d_Material_DIFFUSE:
	device->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE,
			       D3DMCS_COLOR1);
	break;
    case javax_media_j3d_Material_SPECULAR:
	device->SetRenderState(D3DRS_SPECULARMATERIALSOURCE,
			       D3DMCS_COLOR1);
	d3dCtx->resetColorTarget = true;
	break;
    case javax_media_j3d_Material_AMBIENT:
	device->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE,
			       D3DMCS_COLOR1);
	d3dCtx->resetColorTarget = true;
	break;
    case javax_media_j3d_Material_AMBIENT_AND_DIFFUSE:
	device->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE,
			       D3DMCS_COLOR1);
	device->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE,
			       D3DMCS_COLOR1);
	d3dCtx->resetColorTarget = true;
	break;
    case javax_media_j3d_Material_EMISSIVE:
	device->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE,
			       D3DMCS_COLOR1);
	d3dCtx->resetColorTarget = true;
	break;
    default:
	printf("Material updateNative: Uknown colorTarget %d\n", colorTarget);
    }

    material.Power = shininess;

    CopyColor(material.Emissive, eRed, eGreen, eBlue, 1.0f);
    CopyColor(material.Ambient,  aRed, aGreen, aBlue, 1.0f);
    CopyColor(material.Specular, sRed, sGreen, sBlue, 1.0f);

    d3dCtx->currentColor_a = transparency;

    if (lightEnable) {
	d3dCtx->currentColor_r = dRed;
	d3dCtx->currentColor_g = dGreen;
	d3dCtx->currentColor_b = dBlue;

	CopyColor(material.Diffuse,  dRed, dGreen, dBlue,
		  transparency);

    } else {
	d3dCtx->currentColor_r = colorRed;
	d3dCtx->currentColor_g = colorGreen;
	d3dCtx->currentColor_b = colorBlue;

	CopyColor(material.Diffuse, colorRed, colorGreen,
		  colorBlue, transparency);
    }


    d3dCtx->isLightEnable = lightEnable;
    device->SetRenderState(D3DRS_LIGHTING, lightEnable);
    device->SetMaterial(&material);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetTransparency(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint geometryType,
    jint polygonMode,
    jboolean lineAA,
    jboolean pointAA)
{
    GetDevice();

    // Line/Point  Antialiasing not support

    /*
    if (((((geometryType & LINE) != 0) || polygonMode == POLYGON_LINE)
	 && lineAA == JNI_TRUE) ||
        ((((geometryType & _POINT) != 0) || polygonMode == POLYGON_POINT)
	 && pointAA == JNI_TRUE)) {
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    } else {
    */
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	//    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTransparencyAttributes(
    JNIEnv *env,
    jobject tr,
    jlong ctx,
    jfloat transparency,
    jint geometryType,
    jint polygonMode,
    jboolean lineAA,
    jboolean pointAA,
    jint transparencyMode,
    jint srcBlendFunction,
    jint dstBlendFunction)
{

    GetDevice();

    // No screen door transparency in D3D, use BLENDED
    // Don't know how to use STIPPLEDALPHA either.
    /*
    if (transparencyMode != TRANS_SCREEN_DOOR) {
	device->SetRenderState(D3DRS_STIPPLEDALPHA, FALSE);
    } else  {
	device->SetRenderState(D3DRS_STIPPLEDALPHA, TRUE);
    }
    */

    if (transparencyMode < javax_media_j3d_TransparencyAttributes_NONE) {
	/*
	((((geometryType & LINE) != 0) || polygonMode == POLYGON_LINE)
	 && lineAA == JNI_TRUE) ||
        ((((geometryType & _POINT) != 0) || polygonMode == POLYGON_POINT)
	 && pointAA == JNI_TRUE)) {
	*/
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	device->SetRenderState(D3DRS_SRCBLEND,
			       blendFunctionTable[srcBlendFunction]);
	device->SetRenderState(D3DRS_DESTBLEND,
			       blendFunctionTable[dstBlendFunction]);
    } else {
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    }
}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetColoringAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat colorRed,
    jfloat colorGreen,
    jfloat colorBlue,
    jfloat transparency,
    jboolean lightEnable)
{

    GetDevice();

    if (!lightEnable) {
	d3dCtx->currentColor_r = colorRed;
	d3dCtx->currentColor_g = colorGreen;
	d3dCtx->currentColor_b = colorBlue;
	d3dCtx->currentColor_a = transparency;
    }
    device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
    // No line smooth in D3D
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateColoringAttributes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat dRed,
    jfloat dGreen,
    jfloat dBlue,
    jfloat colorRed,
    jfloat colorGreen,
    jfloat colorBlue,
    jfloat transparency,
    jboolean lightEnable,
    jint shadeModel)
{

    GetDevice();

    d3dCtx->currentColor_a = transparency;
    if (lightEnable) {
	d3dCtx->currentColor_r = dRed;
	d3dCtx->currentColor_g = dGreen;
	d3dCtx->currentColor_b = dBlue;
    } else {
	d3dCtx->currentColor_r = colorRed;
	d3dCtx->currentColor_g = colorGreen;
	d3dCtx->currentColor_b = colorBlue;
    }


    if (shadeModel == javax_media_j3d_ColoringAttributes_SHADE_FLAT) {
	device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
    } else {
	device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_resetTextureNative(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint texUnitIndex)
{
    GetDevice();

    if (texUnitIndex < 0) {
	texUnitIndex = 0;
    }

    d3dCtx->texUnitStage = texUnitIndex;

    if (texUnitIndex >= d3dCtx->bindTextureIdLen) {
	return;
    }
    device->SetTexture(texUnitIndex, NULL);
    d3dCtx->bindTextureId[texUnitIndex] = -1;

    if (d3dCtx->texTransformSet[texUnitIndex]) {
	d3dCtx->texTransformSet[texUnitIndex] = false;
	device->SetTransform((D3DTRANSFORMSTATETYPE)
			     (D3DTS_TEXTURE0 + texUnitIndex),
			     &identityMatrix);
    }
    d3dCtx->texGenMode[texUnitIndex] = TEX_GEN_NONE;
    device->SetTextureStageState(texUnitIndex,
				 D3DTSS_TEXCOORDINDEX,
				 D3DTSS_TCI_PASSTHRU);
    device->SetTextureStageState(texUnitIndex,
				 D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    device->SetTextureStageState(texUnitIndex,
				 D3DTSS_COLORARG1, D3DTA_TEXTURE);
    device->SetTextureStageState(texUnitIndex,
				 D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    device->SetTextureStageState(texUnitIndex,
				 D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_bindTexture2D(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint objectId,
    jboolean enable)
{
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    if (!enable) {
	device->SetTexture(d3dCtx->texUnitStage, NULL);
	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = -1;
    } else {
	if (d3dCtx->bindTextureId[d3dCtx->texUnitStage] == objectId) {
	    return;
	}

	if (objectId >= d3dCtx->textureTableLen) {
	    DWORD i;
	    DWORD len = max(objectId+1, d3dCtx->textureTableLen << 1);
	    LPDIRECT3DTEXTURE9 *newTable = (LPDIRECT3DTEXTURE9 *)
		malloc(sizeof(LPDIRECT3DTEXTURE9) * len);

	    if (newTable == NULL) {
		printf("Not enough memory to alloc texture table of size %d.\n", len);
		return;
	    }
	    for (i=0; i < d3dCtx->textureTableLen; i++) {
		newTable[i] = d3dCtx->textureTable[i];
	    }
	    for (i=d3dCtx->textureTableLen; i < len; i++) {
		newTable[i] = NULL;
	    }
	    d3dCtx->textureTableLen = len;
	    SafeFree(d3dCtx->textureTable);
	    d3dCtx->textureTable = newTable;
	}

	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = objectId;
	if (d3dCtx->textureTable[objectId] != NULL) {
	    device->SetTexture(d3dCtx->texUnitStage,
			       d3dCtx->textureTable[objectId]);
	}
	// else we will bind this in updateTextureImage
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DFilterModes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint minFilter,
    jint magFilter)
{
    GetDevice();


    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    d3dCtx->texLinearMode = false;

    /* set texture min filter */
    switch (minFilter) {
        case javax_media_j3d_Texture_FASTEST:
        case javax_media_j3d_Texture_BASE_LEVEL_POINT:
	    device->SetSamplerState(d3dCtx->texUnitStage,
					 D3DSAMP_MINFILTER, D3DTEXF_POINT);
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MIPFILTER, D3DTEXF_POINT);
	    break;
        case javax_media_j3d_Texture_BASE_LEVEL_LINEAR:
                    d3dCtx->texLinearMode = true;
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MIPFILTER, D3DTEXF_POINT);
	    break;
        case javax_media_j3d_Texture_MULTI_LEVEL_POINT:
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MINFILTER, D3DTEXF_POINT);
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	    break;
        case javax_media_j3d_Texture_NICEST:
        case javax_media_j3d_Texture_MULTI_LEVEL_LINEAR:
            d3dCtx->texLinearMode = true;
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	    break;
    }

    /* set texture mag filter */
    switch (magFilter) {
        case javax_media_j3d_Texture_FASTEST:
        case javax_media_j3d_Texture_BASE_LEVEL_POINT:
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MAGFILTER, D3DTEXF_POINT);
	    break;
        case javax_media_j3d_Texture_NICEST:
        case javax_media_j3d_Texture_BASE_LEVEL_LINEAR:
            d3dCtx->texLinearMode = true;
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	    break;
    }

    return;
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DLodRange(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint baseLevel,
    jint maximumLevel,
    jfloat minimumLod,
    jfloat maximumLod)
{
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DLodOffset(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat lodOffsetS,
    jfloat lodOffsetT,
    jfloat lodOffsetR)
{
    /* not supported */
}

void updateTextureBoundary(JNIEnv *env,
			   jobject obj,
			   jlong ctx,
			   jint boundaryModeS,
			   jint boundaryModeT,
			   jint boundaryModeR,
			   jfloat boundaryRed,
			   jfloat boundaryGreen,
			   jfloat boundaryBlue,
			   jfloat boundaryAlpha)
{
    GetDevice();


    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    /* set texture wrap parameter */
    BOOL useBorderMode = FALSE;

    // D3D ignored border color in CLAMP mode.
    // Instead D3D use Border color in D3DTADDRESS_BORDER only.
    // So we approximate the effect by using D3DTADDRESS_BORDER
    // mode if linear filtering mode is used.

    switch (boundaryModeS) {
        case javax_media_j3d_Texture_WRAP:
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_ADDRESSU,
					 D3DTADDRESS_WRAP);
	    break;
        case javax_media_j3d_Texture_CLAMP:
	    if (!d3dCtx->texLinearMode || !d3dCtx->deviceInfo->texBorderModeSupport) {
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSU,
					     D3DTADDRESS_CLAMP);
	    } else {
		useBorderMode = TRUE;
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSU,
					     D3DTADDRESS_BORDER);
	    }
	    break;
    }

    switch (boundaryModeT) {
        case javax_media_j3d_Texture_WRAP:
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_ADDRESSV,
					 D3DTADDRESS_WRAP);
	    break;
        case javax_media_j3d_Texture_CLAMP:
	    if (!d3dCtx->texLinearMode || !d3dCtx->deviceInfo->texBorderModeSupport) {
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSV,
						 D3DTADDRESS_CLAMP);
	    } else {
		useBorderMode = TRUE;
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSV,
					     D3DTADDRESS_BORDER);
	    }
	    break;
    }

    if (boundaryModeR >= 0) {
	switch (boundaryModeR) {
        case javax_media_j3d_Texture_WRAP:
	    device->SetSamplerState (d3dCtx->texUnitStage,
					 D3DSAMP_ADDRESSW,
					 D3DTADDRESS_WRAP);
	    break;
        case javax_media_j3d_Texture_CLAMP:
	    if (!d3dCtx->texLinearMode || !d3dCtx->deviceInfo->texBorderModeSupport) {
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSW,
					     D3DTADDRESS_CLAMP);
	    } else {
		useBorderMode = TRUE;
		device->SetSamplerState (d3dCtx->texUnitStage,
					     D3DSAMP_ADDRESSW,
					     D3DTADDRESS_BORDER);
	    }
	    break;
	}
    }

    if (useBorderMode) {
	D3DCOLOR color = D3DCOLOR_COLORVALUE(boundaryRed, boundaryGreen,
					     boundaryBlue, boundaryAlpha);

	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_BORDERCOLOR,
				      *((DWORD *) &color));
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DBoundary(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint boundaryModeS,
    jint boundaryModeT,
    jfloat boundaryRed,
    jfloat boundaryGreen,
    jfloat boundaryBlue,
    jfloat boundaryAlpha)
{
    updateTextureBoundary(env, obj, ctx, boundaryModeS,
			  boundaryModeT, -1,
			  boundaryRed, boundaryGreen,
			  boundaryBlue, boundaryAlpha);

}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DSharpenFunc(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint numPts,
    jfloatArray pts)
{
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DFilter4Func(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint numPts,
    jfloatArray pts)
{
}


void updateTextureAnisotropicFilter(
    jlong ctx,
    jfloat degree)
{
    GetDevice();

    if (degree > 1) {
	DWORD deg = degree + 0.5f; // round float to int
	// This will overwrite the previous setting in
	// updateTextureFilterModes()
	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_MINFILTER,
				     D3DTEXF_ANISOTROPIC);
	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_MAGFILTER,
				     D3DTEXF_ANISOTROPIC);
	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_MIPFILTER,
				     D3DTEXF_ANISOTROPIC);

	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_MAXANISOTROPY, deg);
    } else {
	// updateTextureFilterModes() will always invoke before
	// updateTextureAnisotropicFilter() to set Filter mode
	// correctly.
	device->SetSamplerState (d3dCtx->texUnitStage,
				     D3DSAMP_MAXANISOTROPY, 1);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DAnisotropicFilter(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat degree)
{
     updateTextureAnisotropicFilter(ctx, degree);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DAnisotropicFilter(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat degree)
{
    GetCtx();

    if (d3dCtx->deviceInfo->maxTextureDepth > 0) {
	updateTextureAnisotropicFilter(ctx, degree);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapAnisotropicFilter(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat degree)
{
     updateTextureAnisotropicFilter(ctx, degree);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DSubImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint level,
    jint xoffset,
    jint yoffset,
    jint textureFormat,
    jint imageFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint tilew,
    jint width,
    jint height,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{
    /* Note: useAutoMipMap is not use for SubImage in the d3d pipe */
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->textureTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateTextureSubImage  bind texture ID %d, textureTableLen %d, texUnitStage = %d \n", currBindTex, d3dCtx->textureTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DTEXTURE9 surf = d3dCtx->textureTable[currBindTex];

    if ((surf == NULL) ||
	((level > 0) && (!d3dCtx->deviceInfo->supportMipmap))) {
	return;
    }

    void *imageObjPtr;
    jbyte *dataBuffer;
    
    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
    }
    else {
	imageObjPtr = (void *)env->GetDirectBufferAddress(data);
    }

    // update Image data
    switch (imageFormat) {
    case IMAGE_FORMAT_BYTE_BGR:         
    case IMAGE_FORMAT_BYTE_RGB:
    case IMAGE_FORMAT_BYTE_ABGR:         
    case IMAGE_FORMAT_BYTE_RGBA:
    case IMAGE_FORMAT_BYTE_LA:
    case IMAGE_FORMAT_BYTE_GRAY: 
    case IMAGE_FORMAT_INT_BGR:         
    case IMAGE_FORMAT_INT_RGB:
    case IMAGE_FORMAT_INT_ARGB:         
	dataBuffer = (jbyte *) imageObjPtr;
	copyDataToSurface(imageFormat, textureFormat, xoffset, yoffset,
			  imgXOffset, imgYOffset,
			  width, height, tilew, dataBuffer,
			  surf, level);
	break;
    case IMAGE_FORMAT_USHORT_GRAY:	    
    default:
	throwAssert(env, "updateTexture2DSubImage : imageFormat illegal format");
	return;
    }
    
    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture2DImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint numLevels,
    jint level,
    jint textureFormat,
    jint imageFormat,
    jint width,
    jint height,
    jint boundaryWidth,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	if (debug) {
	    printf("Internal Error: texUnitState %d, bindTextureIDLen %d\n",
		   d3dCtx->texUnitStage, d3dCtx->bindTextureIdLen);
	}
	return;
    }

    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->textureTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateTextureImage  bind texture ID %d, textureTableLen %d, texUnitStage = %d \n",  currBindTex, d3dCtx->textureTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DTEXTURE9 surf = d3dCtx->textureTable[currBindTex];

    if (level == 0) {
	if (surf != NULL) {
	    // see if no. of mipmap level change

	    if (surf->GetLevelCount() != numLevels) {
		d3dCtx->freeResource(surf);
		d3dCtx->textureTable[currBindTex] = NULL;
		surf = NULL;
	    }
	}

	if (surf == NULL) {
	// Need to create surface
	    surf = createTextureSurface(d3dCtx, numLevels, textureFormat,
					width, height, useAutoMipMap);

	    if (surf == NULL) {
		return;
	    }

	    d3dCtx->textureTable[currBindTex] = surf;
	}
    } else {
	if (surf == NULL) {
	    return;
	}
    }

    if ((level > 0) && (!d3dCtx->deviceInfo->supportMipmap)) {
	if (debug) {
	    printf("mipmap not support\n");
	}
	return;
    }

    // update Image data
    if (data != NULL) {
	void *imageObjPtr;
	jbyte *dataBuffer;

	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
	}
	else {
	    imageObjPtr = (void *)env->GetDirectBufferAddress(data);
	}

	switch (imageFormat) {
        case IMAGE_FORMAT_BYTE_BGR:         
        case IMAGE_FORMAT_BYTE_RGB:
        case IMAGE_FORMAT_BYTE_ABGR:         
        case IMAGE_FORMAT_BYTE_RGBA:
        case IMAGE_FORMAT_BYTE_LA:
        case IMAGE_FORMAT_BYTE_GRAY: 
        case IMAGE_FORMAT_INT_BGR:         
        case IMAGE_FORMAT_INT_RGB:
        case IMAGE_FORMAT_INT_ARGB:         
	    dataBuffer = (jbyte *) imageObjPtr;
	    copyDataToSurface(imageFormat, textureFormat, 0, 0, 0, 0,
			      width, height, width, dataBuffer,
			      surf, level);
	    break;
        case IMAGE_FORMAT_USHORT_GRAY:	    
        default:
            throwAssert(env, "updateTexture2DImage : imageFormat illegal format");
            return;
        }

	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
	}
    }
    device->SetTexture(d3dCtx->texUnitStage, surf);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_bindTexture3D(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint objectId,
    jboolean enable)
{
    GetDevice();

    if ((d3dCtx->deviceInfo->maxTextureDepth <= 0) ||
	(d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen)) {
	return;
    }

    if (!enable) {
	device->SetTexture(d3dCtx->texUnitStage, NULL);
	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = -1;
    } else {
	if (d3dCtx->bindTextureId[d3dCtx->texUnitStage] == objectId) {
	    return;
	}
	if (objectId >= d3dCtx->volumeTableLen) {
	    DWORD i;
	    DWORD len = max(objectId+1, d3dCtx->volumeTableLen << 1);
	    LPDIRECT3DVOLUMETEXTURE9 *newTable = (LPDIRECT3DVOLUMETEXTURE9 *)
		malloc(sizeof(LPDIRECT3DVOLUMETEXTURE9) * len);

	    if (newTable == NULL) {
		printf("Not enough memory to alloc volume texture table of size %d.\n", len);
		return;
	    }
	    for (i=0; i < d3dCtx->volumeTableLen; i++) {
		newTable[i] = d3dCtx->volumeTable[i];
	    }
	    for (i=d3dCtx->volumeTableLen; i < len; i++) {
		newTable[i] = NULL;
	    }
	    d3dCtx->volumeTableLen = len;
	    SafeFree(d3dCtx->volumeTable);
	    d3dCtx->volumeTable = newTable;
	}

	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = objectId;
	if (d3dCtx->volumeTable[objectId] != NULL) {
	    device->SetTexture(d3dCtx->texUnitStage,
			       d3dCtx->volumeTable[objectId]);
	}
	// else we will bind this in updateTextureImage
    }

}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DFilterModes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint minFilter,
    jint magFilter)
{
    GetCtx();

    if (d3dCtx->deviceInfo->maxTextureDepth > 0) {
	Java_javax_media_j3d_NativePipeline_updateTexture2DFilterModes(
			      env, obj, ctx, minFilter, magFilter);
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DLodRange(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint baseLevel,
    jint maximumLevel,
    jfloat minimumLod,
    jfloat maximumLod)
{
    // Not support
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DLodOffset(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat lodOffsetS,
    jfloat lodOffsetT,
    jfloat lodOffsetR)
{
    /* not supported */
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DBoundary(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint boundaryModeS,
    jint boundaryModeT,
    jint boundaryModeR,
    jfloat boundaryRed,
    jfloat boundaryGreen,
    jfloat boundaryBlue,
    jfloat boundaryAlpha)
{

    GetCtx();

    if (d3dCtx->deviceInfo->maxTextureDepth > 0) {

	updateTextureBoundary(
			      env, obj, ctx,
			      boundaryModeS,
			      boundaryModeT,
			      boundaryModeR,
			      boundaryRed,
			      boundaryGreen,
			      boundaryBlue,
			      boundaryAlpha);
    }
}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint numLevels,
    jint level,
    jint textureFormat,
    jint imageFormat,
    jint width,
    jint height,
    jint depth,
    jint boundaryWidth,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{    
    GetDevice();

    if (d3dCtx->deviceInfo->maxTextureDepth <= 0) {
	return;
    }

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	if (debug) {
	    printf("Internal Error: texUnitState %d, bindTextureIDLen %d\n",
		   d3dCtx->texUnitStage, d3dCtx->bindTextureIdLen);
	}
	return;
    }


    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->volumeTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateTexture3DImage  bind texture ID %d, volumeTableLen %d, texUnitStage = %d \n",  currBindTex, d3dCtx->volumeTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DVOLUMETEXTURE9 surf = d3dCtx->volumeTable[currBindTex];

    if (level == 0) {
	if (surf != NULL) {
	    // see if no. of mipmap level change

	    if (surf->GetLevelCount() != numLevels) {
		d3dCtx->freeResource(surf);
		d3dCtx->volumeTable[currBindTex] = NULL;
		surf = NULL;
	    }
	}

	if (surf == NULL) {
	    surf = createVolumeTexture(d3dCtx, numLevels, textureFormat,
				       width, height, depth, useAutoMipMap);
	    if (surf == NULL) {
		return;
	    }

	    d3dCtx->volumeTable[currBindTex] = surf;
	}
    } else {
	if (surf == NULL) {
	    return;
	}
    }

    if ((level > 0) && (!d3dCtx->deviceInfo->supportMipmap)) {
	if (debug) {
	    printf("mipmap not support\n");
	}
	return;
    }

    // update Image data
    if (data != NULL) {
	void *imageObjPtr;
	jbyte *dataBuffer;
	
	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
	}
	else {
	    imageObjPtr = (void *)env->GetDirectBufferAddress(data);
	}

	switch (imageFormat) {
        case IMAGE_FORMAT_BYTE_BGR:         
        case IMAGE_FORMAT_BYTE_RGB:
        case IMAGE_FORMAT_BYTE_ABGR:         
        case IMAGE_FORMAT_BYTE_RGBA:
        case IMAGE_FORMAT_BYTE_LA:
        case IMAGE_FORMAT_BYTE_GRAY: 
        case IMAGE_FORMAT_INT_BGR:         
        case IMAGE_FORMAT_INT_RGB:
        case IMAGE_FORMAT_INT_ARGB:         
	    dataBuffer = (jbyte *) imageObjPtr;
	    copyDataToVolume(imageFormat, textureFormat, 0, 0, 0, 0, 0, 0,
			     width, height, depth, width, height, dataBuffer,
			     surf, level);
	    break;
        case IMAGE_FORMAT_USHORT_GRAY:	    
        default:
            throwAssert(env, "updateTexture3DImage : imageFormat illegal format");
            return;
        }
	
	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
	}
    }
    device->SetTexture(d3dCtx->texUnitStage, surf);

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTexture3DSubImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint level,
    jint xoffset,
    jint yoffset,
    jint zoffset,
    jint textureFormat,
    jint imageFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint imgZOffset,
    jint tilew,
    jint tileh,
    jint width,
    jint height,
    jint depth,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{
    /* Note: useAutoMipMap is not use for SubImage in the d3d pipe */
    
    GetDevice();

    if ((d3dCtx->deviceInfo->maxTextureDepth <= 0) ||
	(d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen)) {
	return;
    }

    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->volumeTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateTexture3DSubImage  bind texture ID %d, volumeableLen %d, texUnitStage = %d \n", currBindTex, d3dCtx->volumeTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DVOLUMETEXTURE9 surf = d3dCtx->volumeTable[currBindTex];

    if ((surf == NULL) ||
	((level > 0) && (!d3dCtx->deviceInfo->supportMipmap))) {
	return;
    }

    void *imageObjPtr;
    jbyte *dataBuffer;
    
    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
    }
    else {
	imageObjPtr = (void *)env->GetDirectBufferAddress(data);
    }

    // update Image data
    switch (imageFormat) {
    case IMAGE_FORMAT_BYTE_BGR:         
    case IMAGE_FORMAT_BYTE_RGB:
    case IMAGE_FORMAT_BYTE_ABGR:         
    case IMAGE_FORMAT_BYTE_RGBA:
    case IMAGE_FORMAT_BYTE_LA:
    case IMAGE_FORMAT_BYTE_GRAY: 
    case IMAGE_FORMAT_INT_BGR:         
    case IMAGE_FORMAT_INT_RGB:
    case IMAGE_FORMAT_INT_ARGB:         
	dataBuffer = (jbyte *) imageObjPtr;
	copyDataToVolume(imageFormat, textureFormat, xoffset,
			 yoffset, zoffset, imgXOffset, imgYOffset,
			 imgZOffset, width, height, depth,
			 tilew, tileh, dataBuffer,
			 surf, level);
 	break;
    case IMAGE_FORMAT_USHORT_GRAY:	    
    default:
	throwAssert(env, "updateTexture3DSubImage : imageFormat illegal format");
	return;
    }

    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_bindTextureCubeMap(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint objectId,
    jboolean enable)
{
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    if (!enable) {
	device->SetTexture(d3dCtx->texUnitStage, NULL);
	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = -1;
    } else {

	if (d3dCtx->bindTextureId[d3dCtx->texUnitStage] == objectId) {
	    return;
	}

	if (objectId >= d3dCtx->cubeMapTableLen) {
	    DWORD i;
	    DWORD len = max(objectId+1, d3dCtx->cubeMapTableLen << 1);
	    LPDIRECT3DCUBETEXTURE9 *newTable = (LPDIRECT3DCUBETEXTURE9 *)
		malloc(sizeof(LPDIRECT3DCUBETEXTURE9) * len);

	    if (newTable == NULL) {
		printf("Not enough memory to alloc cubeMap table of size %d.\n", len);
		return;
	    }
	    for (i=0; i < d3dCtx->cubeMapTableLen; i++) {
		newTable[i] = d3dCtx->cubeMapTable[i];
	    }
	    for (i=d3dCtx->cubeMapTableLen; i < len; i++) {
		newTable[i] = NULL;
	    }
	    d3dCtx->cubeMapTableLen = len;
	    SafeFree(d3dCtx->cubeMapTable);
	    d3dCtx->cubeMapTable = newTable;
	}

	d3dCtx->bindTextureId[d3dCtx->texUnitStage] = objectId;
	if (d3dCtx->cubeMapTable[objectId] != NULL) {
	    device->SetTexture(d3dCtx->texUnitStage,
			       d3dCtx->cubeMapTable[objectId]);
	}
	// else we will bind this in updateTextureImage
    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapFilterModes(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint minFilter,
    jint magFilter)
{
     Java_javax_media_j3d_NativePipeline_updateTexture2DFilterModes(env,
		   obj, ctx, minFilter, magFilter);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapLodRange(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint baseLevel,
    jint maximumLevel,
    jfloat minimumLod,
    jfloat maximumLod)
{
    // not support
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapLodOffset(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat lodOffsetS,
    jfloat lodOffsetT,
    jfloat lodOffsetR)
{
    /* not supported */
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapBoundary(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint boundaryModeS,
    jint boundaryModeT,
    jfloat boundaryRed,
    jfloat boundaryGreen,
    jfloat boundaryBlue,
    jfloat boundaryAlpha)
{
    updateTextureBoundary(env, obj, ctx, boundaryModeS,
			  boundaryModeT, -1, boundaryRed,
			  boundaryGreen, boundaryBlue,
			  boundaryAlpha);

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapSubImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint face,
    jint level,
    jint xoffset,
    jint yoffset,
    jint textureFormat,
    jint imageFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint tilew,
    jint width,
    jint height,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{
    /* Note: useAutoMipMap is not use for SubImage in the d3d pipe */
    
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	return;
    }

    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->cubeMapTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateCubeMapSubImage  bind texture ID %d, cubeMapTableLen %d, texUnitStage = %d \n", currBindTex, d3dCtx->cubeMapTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DCUBETEXTURE9 surf = d3dCtx->cubeMapTable[currBindTex];

    if ((surf == NULL) ||
	((level > 0) && (!d3dCtx->deviceInfo->supportMipmap))) {
	return;
    }

    void *imageObjPtr;
    jbyte *dataBuffer;
    
    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
    }
    else {
	imageObjPtr = (void *)env->GetDirectBufferAddress(data);
    }

    // update Image data
    switch (imageFormat) {
    case IMAGE_FORMAT_BYTE_BGR:         
    case IMAGE_FORMAT_BYTE_RGB:
    case IMAGE_FORMAT_BYTE_ABGR:         
    case IMAGE_FORMAT_BYTE_RGBA:
    case IMAGE_FORMAT_BYTE_LA:
    case IMAGE_FORMAT_BYTE_GRAY: 
    case IMAGE_FORMAT_INT_BGR:         
    case IMAGE_FORMAT_INT_RGB:
    case IMAGE_FORMAT_INT_ARGB:         
	dataBuffer = (jbyte *) imageObjPtr;
	copyDataToCubeMap(imageFormat, textureFormat,
			  xoffset, yoffset,
			  imgXOffset, imgYOffset,
			  width, height,
			  tilew, dataBuffer,
			  surf, level, face);
	break;
    case IMAGE_FORMAT_USHORT_GRAY:	    
    default:
	throwAssert(env, "updateTextureCubeMapSubImage : imageFormat illegal format");
	return;
    }

    if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
       (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
    }   
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureCubeMapImage(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint face,
    jint numLevels,
    jint level,
    jint textureFormat,
    jint imageFormat,
    jint width,
    jint height,
    jint boundaryWidth,
    jint dataType,
    jobject data,
    jboolean useAutoMipMap)
{    
    GetDevice();

    if (d3dCtx->texUnitStage >= d3dCtx->bindTextureIdLen) {
	if (debug) {
	    printf("Internal Error: texUnitState %d, bindTextureIDLen %d\n",
		   d3dCtx->texUnitStage, d3dCtx->bindTextureIdLen);
	}
	return;
    }


    INT currBindTex = d3dCtx->bindTextureId[d3dCtx->texUnitStage];

    if ((currBindTex < 1) ||
	(currBindTex >= d3dCtx->cubeMapTableLen)) {
	if (debug) {
	    printf("Internal Error : UpdateCubeMapImage  bind texture ID %d, cubeMapTableLen %d, texUnitStage = %d \n",  currBindTex, d3dCtx->cubeMapTableLen, d3dCtx->texUnitStage);
	}
	return;
    }

    LPDIRECT3DCUBETEXTURE9 surf = d3dCtx->cubeMapTable[currBindTex];

    if (level == 0) {
	if (surf != NULL) {
	    // see if no. of mipmap level change

	    if (surf->GetLevelCount() != numLevels) {
		d3dCtx->freeResource(surf);
		d3dCtx->cubeMapTable[currBindTex] = NULL;
		surf = NULL;
	    }
	}

	if (surf == NULL) {
	// Need to create surface
	    surf = createCubeMapTexture(d3dCtx, numLevels, textureFormat,
					width, height, useAutoMipMap);
	    if (surf == NULL) {
		return;
	    }

	    d3dCtx->cubeMapTable[currBindTex] = surf;
	}
    } else {
	if (surf == NULL) {
	    return;
	}
    }

    if ((level > 0) && (!d3dCtx->deviceInfo->supportMipmap)) {
	if (debug) {
	    printf("mipmap not support\n");
	}
	return;
    }

    // update Image data
    if (data != NULL) {
	void *imageObjPtr;
	jbyte *dataBuffer;

	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    imageObjPtr = (void *) env->GetPrimitiveArrayCritical((jarray)data, NULL);        
	}
	else {
	    imageObjPtr = (void *)env->GetDirectBufferAddress(data);
	}

	switch (imageFormat) {
        case IMAGE_FORMAT_BYTE_BGR:         
        case IMAGE_FORMAT_BYTE_RGB:
        case IMAGE_FORMAT_BYTE_ABGR:         
        case IMAGE_FORMAT_BYTE_RGBA:
        case IMAGE_FORMAT_BYTE_LA:
        case IMAGE_FORMAT_BYTE_GRAY: 
        case IMAGE_FORMAT_INT_BGR:         
        case IMAGE_FORMAT_INT_RGB:
        case IMAGE_FORMAT_INT_ARGB:         
	    dataBuffer = (jbyte *) imageObjPtr;
	    copyDataToCubeMap(imageFormat, textureFormat, 0, 0, 0, 0,
			      width, height, width, dataBuffer,
			      surf, level, face);
	    break;
        case IMAGE_FORMAT_USHORT_GRAY:	    
        default:
            throwAssert(env, "updateTextureCubeMapImage : imageFormat illegal format");
            return;
        }
	
	if((dataType == IMAGE_DATA_TYPE_BYTE_ARRAY) || 
	   (dataType == IMAGE_DATA_TYPE_INT_ARRAY)) {
	    env->ReleasePrimitiveArrayCritical((jarray)data, imageObjPtr, 0);
	}

    }

    device->SetTexture(d3dCtx->texUnitStage, surf);
}


extern "C" JNIEXPORT
jboolean JNICALL Java_javax_media_j3d_NativePipeline_decal1stChildSetup(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice2();

    device->SetRenderState(D3DRS_STENCILENABLE, TRUE);
    device->Clear(0, NULL, D3DCLEAR_STENCIL, 0, 1.0, 0);
    device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
    device->SetRenderState(D3DRS_STENCILREF, 0x1);
    device->SetRenderState(D3DRS_STENCILMASK, 0x1);
    device->SetRenderState(D3DRS_STENCILFAIL,
			   D3DSTENCILOP_KEEP);
    device->SetRenderState(D3DRS_STENCILZFAIL,
			   D3DSTENCILOP_KEEP);
    device->SetRenderState(D3DRS_STENCILPASS,
			   D3DSTENCILOP_REPLACE);
    return d3dCtx->zEnable;
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_decalNthChildSetup(
    JNIEnv *env,
    jobject obj,
    jlong ctx)

{
    GetDevice();

    d3dCtx->zEnable = FALSE;
    device->SetRenderState(D3DRS_ZENABLE, FALSE);
    device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
    device->SetRenderState(D3DRS_STENCILREF, 0x1);
    device->SetRenderState(D3DRS_STENCILMASK, 0x1);
    device->SetRenderState(D3DRS_STENCILFAIL,
			   D3DSTENCILOP_KEEP);
    device->SetRenderState(D3DRS_STENCILZFAIL,
			   D3DSTENCILOP_KEEP);
    device->SetRenderState(D3DRS_STENCILPASS,
			   D3DSTENCILOP_KEEP);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_decalReset(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean depthBufferEnable)
{
    GetDevice();

    device->SetRenderState(D3DRS_STENCILENABLE, FALSE);

    if (depthBufferEnable) {
	d3dCtx->zEnable = TRUE;
	device->SetRenderState(D3DRS_ZENABLE, TRUE);
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_ctxUpdateEyeLightingEnable(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean localEyeLightingEnable)
{
    GetDevice();
    device->SetRenderState(D3DRS_LOCALVIEWER, localEyeLightingEnable);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_activeTextureUnit(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint index)
{
    GetDevice();
    // If this index is greater than max support stage,
    // then subsequence texture operation will ignore.
    if (index < 0) {
        index = 0;
    }

    d3dCtx->texUnitStage = index;
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateTextureUnitState(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint index,
    jboolean enable)
{
    GetDevice();
    // If this index is greater than max support stage,
    // then subsequence texture operation will ignore.
    if (index <= 0) {
	index = 0;
    }

    d3dCtx->texUnitStage = index;

    if (!enable && (index < d3dCtx->bindTextureIdLen)) {
	device->SetTexture(index, NULL);
	d3dCtx->bindTextureId[index] = -1;
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setDepthFunc(
    JNIEnv * env,
    jobject obj,
    jlong ctx,
    jint func)
{
    GetDevice();

    if (func == javax_media_j3d_RenderingAttributesRetained_LESS) {
	device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);
    } else if (func ==
	       javax_media_j3d_RenderingAttributesRetained_LEQUAL) {
	device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
    }

}



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setBlendColor(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jfloat colorRed,
    jfloat colorGreen,
    jfloat colorBlue,
    jfloat colorAlpha)
{
    // Not support in D3D
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setBlendFunc(
    JNIEnv * env,
    jobject obj,
    jlong ctx,
    jint srcBlendFunction,
    jint dstBlendFunction)
{
    GetDevice();

    device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    device->SetRenderState(D3DRS_SRCBLEND,
			   blendFunctionTable[srcBlendFunction]);
    device->SetRenderState(D3DRS_DESTBLEND,
			   blendFunctionTable[dstBlendFunction]);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_setFogEnableFlag(
    JNIEnv * env,
    jobject obj,
    jlong ctx,
    jboolean enable)
{
    GetDevice();

    device->SetRenderState(D3DRS_FOGENABLE, enable);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_NativePipeline_updateSeparateSpecularColorEnable(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean enable)
{
}


// Fix issue 221 : Temporary stub until Cg is implemented
/*
 * Class:     javax_media_j3d_NativePipeline
 * Method:    loadNativeCgLibrary
 * Signature: ([Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL
Java_javax_media_j3d_NativePipeline_loadNativeCgLibrary(
    JNIEnv *env,
    jobject thiz,
    jobjectArray libpath)
{
    return JNI_FALSE;
}
