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

#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_LinearFogRetained_update(
	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_ExponentialFogRetained_update(
	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_ModelClipRetained_update(
	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_Canvas3D_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_Canvas3D_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_Canvas3D_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;
    }
    D3DVIEWPORT8 vp = {x, y, width, height, 0.0f, 1.0f};

    device->SetViewport(&vp);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_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_Canvas3D_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 & (1<<i));
    }
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_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_Canvas3D_disableFog(
    JNIEnv *env,
    jobject obj,
    jlong ctx)
{
    GetDevice();
    device->SetRenderState(D3DRS_FOGENABLE, false);
}

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



extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_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, TRUE);
    } 

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

    device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
    device->SetRenderState(D3DRS_ALPHAREF, 0);

    /*    setRasterOp(d3dCtx, R2_COPYPEN); */

}

extern "C" JNIEXPORT 
void JNICALL Java_javax_media_j3d_RenderingAttributesRetained_updateNative(
    JNIEnv *env, 
    jobject obj,
    jlong ctx,
    jboolean db_write_enable_override,
    jboolean db_enable_override,
    jboolean db_enable,
    jboolean db_write_enable,
    jfloat at_value,
    jint at_func,
    jboolean ignoreVertexColors,
    jboolean rasterOpEnable,
    jint rasterOp)
{

    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);
    }

    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;
    }



}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_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_ZBIAS, 0);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_PolygonAttributesRetained_updateNative(
    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_ZBIAS, 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");
    }
    */
}



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

    // D3D don't support Line width
    // glLineWidth(1);

    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_LineAttributesRetained_updateNative(
    JNIEnv *env, 
    jobject obj,
    jlong ctx,
    jfloat lineWidth,
    jint linePattern,
    jint linePatternMask,
    jint linePatternScaleFactor,
    jboolean lineAntialiasing)
{
    GetDevice();

    D3DLINEPATTERN pattern;
    
    /*    
    if (lineWidth > 1) {
	if (debug && !isLineWidthMessOutput) {
	    isLineWidthMessOutput = true;
	    printf("Line width > 1 not support !\n");
	}
    }
    */
    // glLineWidth(lineWidth);

    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;
	    }
	}
	*/
        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_Canvas3D_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_PointAttributesRetained_updateNative(
    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_Canvas3D_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_TexCoordGenerationRetained_updateNative(
    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_Canvas3D_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_TextureAttributesRetained_updateNative(
    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_TextureAttributesRetained_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_TextureAttributesRetained_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_TextureAttributesRetained_updateCombinerNative(
    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]);

    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]);

    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_TextureAttributesRetained_updateTextureColorTableNative(
	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_Canvas3D_updateMaterial(
	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_MaterialRetained_updateNative(
        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)
{
    D3DMATERIAL8 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_Canvas3D_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_TransparencyAttributesRetained_updateNative(
    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_Canvas3D_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_ColoringAttributesRetained_updateNative(
    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_Canvas3D_resetTextureNative(
    JNIEnv *env, 
    jobject texture,
    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_TextureRetained_bindTexture(
    JNIEnv *env, 
    jobject texture, 
    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);
	    LPDIRECT3DTEXTURE8 *newTable = (LPDIRECT3DTEXTURE8 *) 
		malloc(sizeof(LPDIRECT3DTEXTURE8) * 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_TextureRetained_updateTextureFilterModes(
    JNIEnv *env, 
    jobject texture, 
    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->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MINFILTER, D3DTEXF_POINT); 
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MIPFILTER, D3DTEXF_POINT); 
	    break;
        case javax_media_j3d_Texture_BASE_LEVEL_LINEAR:
            d3dCtx->texLinearMode = true;
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MINFILTER, D3DTEXF_LINEAR); 
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MIPFILTER, D3DTEXF_POINT); 
	    break;
        case javax_media_j3d_Texture_MULTI_LEVEL_POINT:
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MINFILTER, D3DTEXF_POINT); 
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MIPFILTER, D3DTEXF_LINEAR); 
	    break;
        case javax_media_j3d_Texture_NICEST:
        case javax_media_j3d_Texture_MULTI_LEVEL_LINEAR:
            d3dCtx->texLinearMode = true;
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MINFILTER, D3DTEXF_LINEAR); 
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_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->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MAGFILTER, D3DTEXF_POINT); 
	    break;
        case javax_media_j3d_Texture_NICEST:
        case javax_media_j3d_Texture_BASE_LEVEL_LINEAR:
            d3dCtx->texLinearMode = true;
	    device->SetTextureStageState(d3dCtx->texUnitStage,
					 D3DTSS_MAGFILTER, D3DTEXF_LINEAR); 
	    break;
    }

    return;
}

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


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

void updateTextureBoundary(JNIEnv *env, 
			   jobject texture, 
			   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->SetTextureStageState(d3dCtx->texUnitStage, 
					 D3DTSS_ADDRESSU,
					 D3DTADDRESS_WRAP);
	    break;
        case javax_media_j3d_Texture_CLAMP:
	    if (!d3dCtx->texLinearMode || !d3dCtx->deviceInfo->texBorderModeSupport) {
		device->SetTextureStageState(d3dCtx->texUnitStage,
					     D3DTSS_ADDRESSU,
					     D3DTADDRESS_CLAMP);
	    } else {
		useBorderMode = TRUE;
		device->SetTextureStageState(d3dCtx->texUnitStage,
					     D3DTSS_ADDRESSU,
					     D3DTADDRESS_BORDER);
	    }
	    break;
    }

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

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

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

	device->SetTextureStageState(d3dCtx->texUnitStage,
				     D3DTSS_BORDERCOLOR,
				      *((DWORD *) &color));
    }
}

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

}


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

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureRetained_updateTextureFilter4Func(
    JNIEnv *env, 
    jobject texture,
    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->SetTextureStageState(d3dCtx->texUnitStage,
				     D3DTSS_MINFILTER,
				     D3DTEXF_ANISOTROPIC);
	device->SetTextureStageState(d3dCtx->texUnitStage,
				     D3DTSS_MAGFILTER,
				     D3DTEXF_ANISOTROPIC);
	device->SetTextureStageState(d3dCtx->texUnitStage,
				     D3DTSS_MIPFILTER,
				     D3DTEXF_ANISOTROPIC);

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

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


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

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

}

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


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureRetained_updateTextureSubImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint level,
    jint xoffset,
    jint yoffset,
    jint internalFormat, 
    jint storedFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint tilew,
    jint width, 
    jint height,
    jbyteArray image) 
{
    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;
    }

    LPDIRECT3DTEXTURE8 surf = d3dCtx->textureTable[currBindTex];

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

    // update Image data
    if (storedFormat != FORMAT_USHORT_GRAY) {
	jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(image,   NULL);
	copyDataToSurface(storedFormat, internalFormat, xoffset, yoffset, 
			  imgXOffset, imgYOffset, 
			  width, height, tilew, byteData, 
			  surf, level);
	env->ReleasePrimitiveArrayCritical(image, byteData, 0);

    } else {
	jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(image, NULL);
	copyDataToSurface(storedFormat, internalFormat, xoffset, yoffset,
			  imgXOffset, imgYOffset,
			  width, height, tilew, shortData, 
			  surf, level);
	env->ReleasePrimitiveArrayCritical(image, shortData, 0);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureRetained_updateTextureImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint numLevels,
    jint level,
    jint internalFormat, 
    jint format, 
    jint width, 
    jint height, 
    jint boundaryWidth,
    jbyteArray imageYup)
{
    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;
    }

    LPDIRECT3DTEXTURE8 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, internalFormat, 
					width, height);

	    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 (imageYup != NULL) {
	if (format != FORMAT_USHORT_GRAY) {
	    jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(imageYup,   NULL);
	    copyDataToSurface(format, internalFormat, 0, 0, 0, 0,
			      width, height, width, byteData, 
			      surf, level);
	    env->ReleasePrimitiveArrayCritical(imageYup, byteData, 0);
	    
	} else {
	    jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(imageYup, NULL);
	    copyDataToSurface(format, internalFormat, 0, 0, 0, 0,
			      width, height, width,  shortData, 
			      surf, level);
	    env->ReleasePrimitiveArrayCritical(imageYup, shortData, 0);
	}
    }


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

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture2DRetained_bindTexture(
    JNIEnv *env, 
    jobject texture, 
    jlong ctx,
    jint objectId,
    jboolean enable) 
{
    Java_javax_media_j3d_TextureRetained_bindTexture(env, texture,
			ctx, objectId, enable);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture2DRetained_updateTextureSubImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint level,
    jint xoffset,
    jint yoffset,
    jint internalFormat, 
    jint storedFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint tilew,
    jint width, 
    jint height,
    jbyteArray image) 
{
    Java_javax_media_j3d_TextureRetained_updateTextureSubImage(
	env, texture, ctx, level, xoffset, yoffset, internalFormat,
	storedFormat, imgXOffset, imgYOffset, tilew, width, height, image);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture2DRetained_updateTextureImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint numLevels,
    jint level,
    jint internalFormat, 
    jint format, 
    jint width, 
    jint height, 
    jint boundaryWidth,
    jbyteArray imageYup)
{
    Java_javax_media_j3d_TextureRetained_updateTextureImage(env, texture,
		ctx, numLevels, level, internalFormat, format, 
		width, height, boundaryWidth, imageYup);
}


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture2DRetained_updateDetailTextureParameters(
    JNIEnv *env, 
    jobject texture, 
    jlong ctx,
    jint detailTextureMode,
    jint detailTextureLevel,
    jint numPts,
    jfloatArray funcPts) 
{
    // Not support
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture3DRetained_bindTexture(
    JNIEnv *env, 
    jobject texture, 
    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);
	    LPDIRECT3DVOLUMETEXTURE8 *newTable = (LPDIRECT3DVOLUMETEXTURE8 *) 
		malloc(sizeof(LPDIRECT3DVOLUMETEXTURE8) * 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_Texture3DRetained_updateTextureFilterModes(
    JNIEnv *env, 
    jobject texture, 
    jlong ctx,
    jint minFilter,
    jint magFilter) 
{
    GetCtx();

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

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


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


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

    GetCtx();

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

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


								       
extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture3DRetained_updateTextureImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint numLevels,
    jint level,
    jint internalFormat, 
    jint format, 
    jint width, 
    jint height, 
    jint depth,
    jint boundaryWidth,
    jbyteArray imageYup)
{

    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;
    }

    LPDIRECT3DVOLUMETEXTURE8 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, internalFormat, 
				       width, height, depth);
	    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 (imageYup != NULL) {
	if (format != FORMAT_USHORT_GRAY) {
	    jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(imageYup,   NULL);
	    copyDataToVolume(format, internalFormat, 0, 0, 0, 0, 0, 0,
			     width, height, depth, width, height, byteData, 
			     surf, level);
	    env->ReleasePrimitiveArrayCritical(imageYup, byteData, 0);
	    
	} else {
	    jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(imageYup, NULL);
	    copyDataToVolume(format, internalFormat, 0, 0, 0, 0, 0, 0,
			      width, height, depth, width, height, shortData, 
			      surf, level);
	    env->ReleasePrimitiveArrayCritical(imageYup, shortData, 0);
	}
    }
    device->SetTexture(d3dCtx->texUnitStage, surf);

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Texture3DRetained_updateTextureSubImage(
    JNIEnv *env,
    jobject texture,
    jlong ctx,
    jint level,
    jint xoffset,
    jint yoffset,
    jint zoffset,
    jint internalFormat,
    jint storedFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint imgZOffset,
    jint tilew,
    jint tileh,
    jint width,
    jint height,
    jint depth,
    jbyteArray image)
{
    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;
    }

    LPDIRECT3DVOLUMETEXTURE8 surf = d3dCtx->volumeTable[currBindTex];

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

    // update Image data
    if (storedFormat != FORMAT_USHORT_GRAY) {
	jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(image,   NULL);
	copyDataToVolume(storedFormat, internalFormat, xoffset,
			 yoffset, zoffset, imgXOffset, imgYOffset, 
			 imgZOffset, width, height, depth, 
			 tilew, tileh, byteData, 
			 surf, level);
	env->ReleasePrimitiveArrayCritical(image, byteData, 0);

    } else {
	jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(image, NULL);
	copyDataToVolume(storedFormat, internalFormat, xoffset,
			 yoffset, zoffset,
			 imgXOffset, imgYOffset, imgZOffset,
			 width, height, depth, tilew, tileh, shortData, 
			 surf, level);
	env->ReleasePrimitiveArrayCritical(image, shortData, 0);
    }

}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureCubeMapRetained_bindTexture(
    JNIEnv *env,
    jobject texture,
    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);
	    LPDIRECT3DCUBETEXTURE8 *newTable = (LPDIRECT3DCUBETEXTURE8 *) 
		malloc(sizeof(LPDIRECT3DCUBETEXTURE8) * 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_TextureCubeMapRetained_updateTextureFilterModes(
    JNIEnv *env,
    jobject texture,
    jlong ctx,
    jint minFilter,
    jint magFilter)
{
     Java_javax_media_j3d_TextureRetained_updateTextureFilterModes(env, 
		   texture, ctx, minFilter, magFilter);
}


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


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


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

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureCubeMapRetained_updateTextureSubImage(
    JNIEnv *env,
    jobject texture,
    jlong ctx,
    jint face,
    jint level,
    jint xoffset,
    jint yoffset,
    jint internalFormat,
    jint storedFormat,
    jint imgXOffset,
    jint imgYOffset,
    jint tilew,
    jint width,
    jint height,
    jbyteArray image) 
{
    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;
    }

    LPDIRECT3DCUBETEXTURE8 surf = d3dCtx->cubeMapTable[currBindTex];

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

    // update Image data
    if (storedFormat != FORMAT_USHORT_GRAY) {
	jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(image,   NULL);
	copyDataToCubeMap(storedFormat, internalFormat, 
			  xoffset, yoffset, 
			  imgXOffset, imgYOffset, 
			  width, height,
			  tilew, byteData, 
			  surf, level, face);
	env->ReleasePrimitiveArrayCritical(image, byteData, 0);

    } else {
	jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(image, NULL);
	copyDataToCubeMap(storedFormat, internalFormat, 
			  xoffset, yoffset, 
			  imgXOffset, imgYOffset, 
			  width, height, 
			  tilew, shortData, 
			  surf, level, face);
	env->ReleasePrimitiveArrayCritical(image, shortData, 0);
    }
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_TextureCubeMapRetained_updateTextureImage(
    JNIEnv *env,
    jobject texture,
    jlong ctx,
    jint face,
    jint numLevels,
    jint level,
    jint internalFormat,
    jint format,
    jint width,
    jint height,
    jint boundaryWidth,
    jbyteArray imageYup)
{
    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;
    }

    LPDIRECT3DCUBETEXTURE8 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, internalFormat, 
					width, height);
	    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 (imageYup != NULL) {
	if (format != FORMAT_USHORT_GRAY) {
	    jbyte *byteData = (jbyte *) env->GetPrimitiveArrayCritical(imageYup,   NULL);
	    copyDataToCubeMap(format, internalFormat, 0, 0, 0, 0,
			      width, height, width, byteData, 
			      surf, level, face);
	    env->ReleasePrimitiveArrayCritical(imageYup, byteData, 0);
	    
	} else {
	    jshort *shortData = (jshort *) env->GetPrimitiveArrayCritical(imageYup, NULL);
	    copyDataToCubeMap(format, internalFormat, 0, 0, 0, 0,
			      width, height, width,  shortData, 
			      surf, level, face);
	    env->ReleasePrimitiveArrayCritical(imageYup, shortData, 0);
	}
    }

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


extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_DetailTextureImage_bindTexture(
    JNIEnv *env, 
    jobject texture, 
    jlong ctx,
    jint objectId)
{
    // NOT SUPPORTED
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_DetailTextureImage_updateTextureImage(
    JNIEnv *env, 
    jobject texture,
    jlong ctx,
    jint numLevels,
    jint level,
    jint internalFormat, 
    jint format, 
    jint width, 
    jint height, 
    jint boundaryWidth,
    jbyteArray imageYup)
{
    // NOT SUPPORTED
}

extern "C" JNIEXPORT
jboolean JNICALL Java_javax_media_j3d_Canvas3D_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_Canvas3D_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_Canvas3D_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_Canvas3D_ctxUpdateEyeLightingEnable(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jboolean localEyeLightingEnable)
{
    GetDevice();
    device->SetRenderState(D3DRS_LOCALVIEWER, localEyeLightingEnable);
}

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_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_TextureUnitStateRetained_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_Canvas3D_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_Canvas3D_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_Canvas3D_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_Canvas3D_setFogEnableFlag(
    JNIEnv * env, 
    jobject obj, 
    jlong ctx,
    jboolean enable)
{
    GetDevice();

    device->SetRenderState(D3DRS_FOGENABLE, enable);
}

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

extern "C" JNIEXPORT
void JNICALL Java_javax_media_j3d_Canvas3D_updateTexUnitStateMap(
    JNIEnv *env,
    jobject obj,
    jlong ctx,
    jint numActiveTexUnit,
    jintArray texUnitStateMapArray)
{
    if ((texUnitStateMapArray != NULL) && (numActiveTexUnit > 0)) {
	GetDevice();

        jint* texUnitStateMap = (jint *) env->GetPrimitiveArrayCritical(
                                                texUnitStateMapArray, NULL);
	int genMode;
	int ts;
	for (int i = 0; i < numActiveTexUnit; i++) {
	    genMode = setTextureStage(d3dCtx, device, i, texUnitStateMap[i]);
	    if (genMode != TEX_GEN_AUTO) {
		ts = d3dCtx->texStride[i];
		if (ts == 0) {
		    /*
		      In multiTexture case when no tex defined in non object
		      linear mode.
		    */
		    ts = d3dCtx->texCoordFormat[i];
		}
	    } else {
		ts = d3dCtx->texCoordFormat[i];
	    }
	    setTexTransformStageFlag(d3dCtx, device, i, ts, genMode);
	}

        env->ReleasePrimitiveArrayCritical(texUnitStateMapArray,
                                                texUnitStateMap, 0);
    }
}