/* Automatically generated from Squeak on #(21 October 2001 9:19:22 pm) */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Default EXPORT macro that does nothing (see comment in sq.h): */
#define EXPORT(returnType) returnType

/* Do not include the entire sq.h file but just those parts needed. */
/*  The virtual machine proxy definition */
#include "sqVirtualMachine.h"
/* Configuration options */
#include "sqConfig.h"
/* Platform specific definitions */
#include "sqPlatformSpecific.h"

#define true 1
#define false 0
#define null 0  /* using 'null' because nil is predefined in Think C */
#include "b3d.h"

/* memory access macros */
#define byteAt(i) (*((unsigned char *) (i)))
#define byteAtput(i, val) (*((unsigned char *) (i)) = val)
#define longAt(i) (*((int *) (i)))
#define longAtput(i, val) (*((int *) (i)) = val)

/*** Variables ***/
static char bbPluginName[256] = "BitBltPlugin";
static int copyBitsFn;
static struct VirtualMachine* interpreterProxy;
static double l2vDirection[3];
static double l2vDistance;
static double l2vSpecDir[3];
static int lightFlags;
static double lightScale;
static float* litVertex;
static int loadBBFn;
static const char *moduleName = "Squeak3D 21 October 2001 (i)";
static float* primLight;
static float* primMaterial;
static B3DRasterizerState state;
static int vbFlags;
static B3DPrimitiveViewport viewport;
static double vtxInColor[4];
static double vtxOutColor[4];

/*** Function Prototypes ***/
static int addPartfromtrackFlagscale(float *lightPart, float *materialPart, int vbTrackFlag, double scale);
static int analyzeMatrix3x3Length(float *m);
static int analyzeMatrix(float *m);
#pragma export on
EXPORT(int) Squeak3D_b3dClipPolygon(void);
EXPORT(int) Squeak3D_b3dComputeMinIndexZ(void);
EXPORT(int) Squeak3D_b3dComputeMinZ(void);
EXPORT(int) Squeak3D_b3dDetermineClipFlags(void);
EXPORT(int) Squeak3D_b3dInitPrimitiveObject(void);
EXPORT(int) Squeak3D_b3dInitializeRasterizerState(void);
EXPORT(int) Squeak3D_b3dInplaceHouseHolderInvert(void);
EXPORT(int) Squeak3D_b3dLoadIndexArray(void);
EXPORT(int) Squeak3D_b3dLoadVertexBuffer(void);
EXPORT(int) Squeak3D_b3dMapVertexBuffer(void);
EXPORT(int) Squeak3D_b3dPrimitiveNextClippedTriangle(void);
EXPORT(int) Squeak3D_b3dPrimitiveObjectSize(void);
EXPORT(int) Squeak3D_b3dPrimitiveTextureSize(void);
EXPORT(int) Squeak3D_b3dRasterizerVersion(void);
EXPORT(int) Squeak3D_b3dShadeVertexBuffer(void);
EXPORT(int) Squeak3D_b3dShaderVersion(void);
EXPORT(int) Squeak3D_b3dStartRasterizer(void);
EXPORT(int) Squeak3D_b3dTransformMatrixWithInto(void);
EXPORT(int) Squeak3D_b3dTransformPrimitiveNormal(void);
EXPORT(int) Squeak3D_b3dTransformPrimitivePosition(void);
EXPORT(int) Squeak3D_b3dTransformPrimitiveRasterPosition(void);
EXPORT(int) Squeak3D_b3dTransformVertexBuffer(void);
EXPORT(int) Squeak3D_b3dTransformerVersion(void);
#pragma export off
static double backClipValueFromto(int last, int next);
static double bottomClipValueFromto(int last, int next);
static int clipPolygoncountwithmask(int *vtxArray, int vtxCount, int *tempVtxArray, int outMask);
static int clipPolygonBackFromtocount(int *buf1, int *buf2, int n);
static int clipPolygonBottomFromtocount(int *buf1, int *buf2, int n);
static int clipPolygonFrontFromtocount(int *buf1, int *buf2, int n);
static int clipPolygonLeftFromtocount(int *buf1, int *buf2, int n);
static int clipPolygonRightFromtocount(int *buf1, int *buf2, int n);
static int clipPolygonTopFromtocount(int *buf1, int *buf2, int n);
static int computeAttenuation(void);
static int computeDirection(void);
static int computeSpecularDirection(void);
static double computeSpotFactor(void);
static int determineClipFlagscount(void *vtxArray, int count);
static double dotProductOfFloatwithDouble(float * v1, double *v2);
static void* fetchLightSourceofObject(int index, int anArray);
static double frontClipValueFromto(int last, int next);
#pragma export on
EXPORT(const char*) Squeak3D_getModuleName(void);
EXPORT(int) Squeak3D_initialiseModule(void);
#pragma export off
static int interpolateFromtoatinto(float *last, float *next, double t, float *out);
static double inverseLengthOfDouble(double * aVector);
static double inverseLengthOfFloat(float * aVector);
static double leftClipValueFromto(int last, int next);
static int loadObjectsFrom(int stackIndex);
static int loadPrimitiveLightSource(void);
static int loadPrimitiveVertex(void);
static int loadRasterizerState(int stackIndex);
static int loadTextureinto(int textureOop, B3DTexture *destPtr);
static int loadTexturesFrom(int stackIndex);
static int loadViewportFrom(int stackIndex);
static int mapVBofSizeinto(void *vtxArray, int vtxCount, int boxArray);
#pragma export on
EXPORT(int) Squeak3D_moduleUnloaded(char * aModuleName);
EXPORT(int) Squeak3D_primitiveSetBitBltPlugin(void);
#pragma export off
static double processIndexedofSizeidxArrayidxSize(float *vtxArray, int vtxSize, int *idxArray, int idxSize);
static int processIndexedIDXofSizeidxArrayidxSize(float *vtxArray, int vtxSize, int *idxArray, int idxSize);
static double processNonIndexedofSize(float *vtxArray, int vtxSize);
static int processNonIndexedIDXofSize(float *vtxArray, int vtxSize);
static double rightClipValueFromto(int last, int next);
#pragma export on
EXPORT(int) Squeak3D_setInterpreter(struct VirtualMachine* anInterpreter);
#pragma export off
static int shadeVertex(void);
static int stackLightArrayValue(int stackIndex);
static void * stackMaterialValue(int stackIndex);
static void* stackMatrix(int index);
static void* stackPrimitiveIndexArrayofSizevalidateforVertexSize(int stackIndex, int nItems, int aBool, int maxIndex);
static void* stackPrimitiveVertex(int index);
static void* stackPrimitiveVertexArrayofSize(int index, int nItems);
static int storeObjectsInto(int stackIndex);
static int storePrimitiveVertex(void);
static double topClipValueFromto(int last, int next);
static int transformMatrixwithinto(float *src, float *arg, float *dst);
static int transformPrimitiveNormalbyrescale(float *pVertex, float *matrix, int rescale);
static int transformPrimitivePositionby(float *pVertex, float *matrix);
static int transformPrimitivePositionFastby(float *pVertex, float *matrix);
static int transformPrimitivePositionFasterby(float *pVertex, float *matrix);
static int transformPrimitiveRasterPositionby(float *pVertex, float *matrix);
static int transformVBcountbyandflags(float *vtxArray, int vtxCount, float *modelViewMatrix, float *projectionMatrix, int flags);
static void* vbLoadArraysize(int oop, int count);


/*	Add the given light part to the output color, scaled by the given scale factor.
	If the given flag is set in vbFlags then load the part from the primitive vertex */

static int addPartfromtrackFlagscale(float *lightPart, float *materialPart, int vbTrackFlag, double scale) {
    double rPart;
    double bPart;
    double aPart;
    double gPart;

	if (vbFlags & vbTrackFlag) {
		rPart = ((vtxInColor[0]) * (lightPart[0])) * scale;
		gPart = ((vtxInColor[1]) * (lightPart[1])) * scale;
		bPart = ((vtxInColor[2]) * (lightPart[2])) * scale;
		aPart = ((vtxInColor[3]) * (lightPart[3])) * scale;
	} else {
		rPart = ((materialPart[0]) * (lightPart[0])) * scale;
		gPart = ((materialPart[1]) * (lightPart[1])) * scale;
		bPart = ((materialPart[2]) * (lightPart[2])) * scale;
		aPart = ((materialPart[3]) * (lightPart[3])) * scale;
	}
	vtxOutColor[0] = ((vtxOutColor[0]) + rPart);
	vtxOutColor[1] = ((vtxOutColor[1]) + gPart);
	vtxOutColor[2] = ((vtxOutColor[2]) + bPart);
	vtxOutColor[3] = ((vtxOutColor[3]) + aPart);
}


/*	Check if the matrix scales normals to non-unit length. */

static int analyzeMatrix3x3Length(float *m) {
    double det;

	det = (((((((m[0]) * (m[5])) * (m[10])) - (((m[2]) * (m[5])) * (m[8]))) + (((m[4]) * (m[9])) * (m[2]))) - (((m[6]) * (m[9])) * (m[0]))) + (((m[8]) * (m[1])) * (m[6]))) - (((m[10]) * (m[1])) * (m[4]));
	return (det < 0.99) || (det > 1.01);
}


/*	Analyze the matrix and return the appropriate flags */

static int analyzeMatrix(float *m) {
    int flags;

	flags = 0;
	if (((m[12]) == 0.0) && (((m[13]) == 0.0) && (((m[14]) == 0.0) && ((m[15]) == 1.0)))) {

		/* Check translation */

		flags = flags | 2;
		if (((m[3]) == 0.0) && (((m[7]) == 0.0) && ((m[11]) == 0.0))) {

			/* Check for identity */

			flags = flags | 4;
			if (((m[0]) == 1.0) && (((m[5]) == 1.0) && (((m[10]) == 1.0) && (((m[1]) == 0.0) && (((m[2]) == 0.0) && (((m[4]) == 0.0) && (((m[6]) == 0.0) && (((m[8]) == 0.0) && ((m[9]) == 0.0))))))))) {
				flags = flags | 1;
			}
		}
	}
	return flags;
}


/*	Primitive. Clip the polygon given in the vertexArray using the temporary vertex array which is assumed to have sufficient size. */

EXPORT(int) Squeak3D_b3dClipPolygon(void) {
    int outMask;
    int *vtxArray;
    int *tempVtxArray;
    int vtxCount;
    int count;

	if (!((interpreterProxy->methodArgumentCount()) == 4)) {
		return interpreterProxy->primitiveFail();
	}
	outMask = interpreterProxy->stackIntegerValue(0);
	vtxCount = interpreterProxy->stackIntegerValue(2);
	vtxArray = stackPrimitiveVertexArrayofSize(3, vtxCount + 4);
	tempVtxArray = stackPrimitiveVertexArrayofSize(1, vtxCount + 4);
	if ((vtxArray == null) || ((tempVtxArray == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	vtxArray -= 16;
	tempVtxArray -= 16;
	count = clipPolygoncountwithmask(vtxArray, vtxCount, tempVtxArray, outMask);
	interpreterProxy->pop(5);
	interpreterProxy->pushInteger(count);
}


/*	Primitive. Compute and return the index for the minimal z value of all objects in the vertex buffer. */

EXPORT(int) Squeak3D_b3dComputeMinIndexZ(void) {
    int vtxSize;
    int primType;
    float *vtxArray;
    int idxSize;
    int *idxArray;
    int minIndex;

	if (!((interpreterProxy->methodArgumentCount()) == 5)) {
		return interpreterProxy->primitiveFail();
	}
	idxSize = interpreterProxy->stackIntegerValue(0);
	vtxSize = interpreterProxy->stackIntegerValue(2);
	primType = interpreterProxy->stackIntegerValue(4);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxArray = stackPrimitiveVertexArrayofSize(3, vtxSize);
	idxArray = stackPrimitiveIndexArrayofSizevalidateforVertexSize(1, idxSize, 1, vtxSize);
	if ((vtxArray == null) || ((idxArray == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	if ((primType < 1) || (primType > 6)) {
		return interpreterProxy->primitiveFail();
	}
	if (primType <= 3) {
		minIndex = processNonIndexedIDXofSize(vtxArray, vtxSize);
	} else {
		minIndex = processIndexedIDXofSizeidxArrayidxSize(vtxArray, vtxSize, idxArray, idxSize);
	}
	if (!(interpreterProxy->failed())) {
		interpreterProxy->pop(6);
		interpreterProxy->pushInteger(minIndex);
	}
}


/*	Primitive. Compute and return the minimal z value of all objects in the vertex buffer. */

EXPORT(int) Squeak3D_b3dComputeMinZ(void) {
    int vtxSize;
    int primType;
    float *vtxArray;
    int idxSize;
    int *idxArray;
    double minZ;

	if (!((interpreterProxy->methodArgumentCount()) == 5)) {
		return interpreterProxy->primitiveFail();
	}
	idxSize = interpreterProxy->stackIntegerValue(0);
	vtxSize = interpreterProxy->stackIntegerValue(2);
	primType = interpreterProxy->stackIntegerValue(4);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxArray = stackPrimitiveVertexArrayofSize(3, vtxSize);
	idxArray = stackPrimitiveIndexArrayofSizevalidateforVertexSize(1, idxSize, 1, vtxSize);
	if ((vtxArray == null) || ((idxArray == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	if ((primType < 1) || (primType > 6)) {
		return interpreterProxy->primitiveFail();
	}
	if (primType <= 3) {
		minZ = processNonIndexedofSize(vtxArray, vtxSize);
	} else {
		minZ = processIndexedofSizeidxArrayidxSize(vtxArray, vtxSize, idxArray, idxSize);
	}
	if (!(interpreterProxy->failed())) {
		interpreterProxy->pop(6);
		interpreterProxy->pushFloat(minZ);
	}
}


/*	Primitive. Determine the clipping flags for all vertices. */

EXPORT(int) Squeak3D_b3dDetermineClipFlags(void) {
    void *vtxArray;
    int vtxCount;
    int result;

	if (!((interpreterProxy->methodArgumentCount()) == 2)) {
		return interpreterProxy->primitiveFail();
	}
	vtxCount = interpreterProxy->stackIntegerValue(0);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxArray = stackPrimitiveVertexArrayofSize(1, vtxCount);
	if ((vtxArray == null) || (interpreterProxy->failed())) {
		return interpreterProxy->primitiveFail();
	}
	result = determineClipFlagscount(vtxArray, vtxCount);
	if (!(interpreterProxy->failed())) {
		interpreterProxy->pop(3);
		interpreterProxy->pushInteger(result);
	}
}

EXPORT(int) Squeak3D_b3dInitPrimitiveObject(void) {
    int primSize;
    int vtxSize;
    int idxSize;
    int primitive;
    int *idxArray;
    void *primObj;
    int *vtxArray;
    int textureIndex;
    int primOop;

	if (!((interpreterProxy->methodArgumentCount()) == 8)) {
		return interpreterProxy->primitiveFail();
	}
	textureIndex = interpreterProxy->stackIntegerValue(0);
	if (interpreterProxy->failed()) {
		return null;
	}
	loadViewportFrom(1);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxSize = interpreterProxy->stackIntegerValue(4);
	vtxArray = stackPrimitiveVertexArrayofSize(5, vtxSize);
	if (vtxArray == null) {
		return interpreterProxy->primitiveFail();
	}
	idxSize = interpreterProxy->stackIntegerValue(2);
	idxArray = stackPrimitiveIndexArrayofSizevalidateforVertexSize(3, idxSize, 1, vtxSize);
	if (idxArray == null) {
		return interpreterProxy->primitiveFail();
	}
	primitive = interpreterProxy->stackIntegerValue(6);
	if ((primitive < 1) || (primitive > 6)) {
		return interpreterProxy->primitiveFail();
	}
	if (!((primitive == 3) || ((primitive == 5) || (primitive == 6)))) {
		return interpreterProxy->primitiveFail();
	}
	primOop = interpreterProxy->stackObjectValue(7);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!(interpreterProxy->isWords(primOop))) {
		return interpreterProxy->primitiveFail();
	}
	primObj = interpreterProxy->firstIndexableField(primOop);

	/* Do the work */

	primSize = interpreterProxy->byteSizeOf(primOop);
	if (primitive == 3) {
		if (b3dAddPolygonObject((void*) primObj, primSize, B3D_FACE_RGB, textureIndex, (B3DPrimitiveVertex*) vtxArray, vtxSize, &viewport) != B3D_NO_ERROR) {
			return interpreterProxy->primitiveFail();
		}
	}
	if (primitive == 5) {
		if (b3dAddIndexedTriangleObject((void*) primObj, primSize, B3D_FACE_RGB, textureIndex, (B3DPrimitiveVertex*) vtxArray, vtxSize, (B3DInputFace*) idxArray, idxSize / 3, &viewport) != B3D_NO_ERROR) {
			return interpreterProxy->primitiveFail();
		}
	}
	if (primitive == 6) {
		if (b3dAddIndexedQuadObject((void*) primObj, primSize, B3D_FACE_RGB, textureIndex, (B3DPrimitiveVertex*) vtxArray, vtxSize, (B3DInputQuad*) idxArray, idxSize / 4, &viewport) != B3D_NO_ERROR) {
			return interpreterProxy->primitiveFail();
		}
	}
	interpreterProxy->pop(9);
	interpreterProxy->push(primOop);
}


/*	Primitive. Initialize the primitive level objects of the given rasterizer. */

EXPORT(int) Squeak3D_b3dInitializeRasterizerState(void) {
    int stateOop;
    int objOop;
    int objLen;
    void *obj;

	if (!((interpreterProxy->methodArgumentCount()) == 0)) {
		return interpreterProxy->primitiveFail();
	}
	stateOop = interpreterProxy->stackObjectValue(0);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!((interpreterProxy->isPointers(stateOop)) && ((interpreterProxy->slotSizeOf(stateOop)) >= 7))) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(0, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeFaceAllocator(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(1, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeEdgeAllocator(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(2, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeAttrAllocator(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(3, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeAET(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(4, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeEdgeList(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
	objOop = interpreterProxy->fetchPointerofObject(5, stateOop);
	if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
		return interpreterProxy->primitiveFail();
	}
	objLen = interpreterProxy->byteSizeOf(objOop);
	obj = interpreterProxy->firstIndexableField(objOop);
	if (b3dInitializeFillList(obj, objLen) != B3D_NO_ERROR) {
		return interpreterProxy->primitiveFail();
	}
}


/*	Primitive. Perform an inplace house holder matrix inversion */

EXPORT(int) Squeak3D_b3dInplaceHouseHolderInvert(void) {
    double d[4][4];
    double m[4][4];
    double x[4][4] = { {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1} };
    double beta;
    int i;
    double sigma;
    double s;
    int j;
    float *rcvr;
    int r;
    int k;
    double sum;

	;
	rcvr = stackMatrix(0);
	for (i = 0; i <= 3; i += 1) {
		for (j = 0; j <= 3; j += 1) {
			(m[i])[j] = (rcvr[(i * 4) + j]);
		}
	}
	for (j = 0; j <= 3; j += 1) {
		sigma = 0.0;
		for (i = j; i <= 3; i += 1) {
			sigma += ((m[i])[j]) * ((m[i])[j]);
		}
		if (sigma < 1.0e-10) {
			return interpreterProxy->primitiveFail();
		}
		if (((m[j])[j]) < 0.0) {
			s = sqrt(sigma);
		} else {
			s = 0.0 - (sqrt(sigma));
		}
		for (r = 0; r <= 3; r += 1) {
			(d[j])[r] = s;
		}
		beta = 1.0 / ((s * ((m[j])[j])) - sigma);
		(m[j])[j] = (((m[j])[j]) - s);
		for (k = (j + 1); k <= 3; k += 1) {
			sum = 0.0;
			for (i = j; i <= 3; i += 1) {
				sum += ((m[i])[j]) * ((m[i])[k]);
			}
			sum = sum * beta;
			for (i = j; i <= 3; i += 1) {
				(m[i])[k] = (((m[i])[k]) + (((m[i])[j]) * sum));
			}
		}
		for (r = 0; r <= 3; r += 1) {
			sum = 0.0;
			for (i = j; i <= 3; i += 1) {
				sum += ((x[i])[r]) * ((m[i])[j]);
			}
			sum = sum * beta;
			for (i = j; i <= 3; i += 1) {
				(x[i])[r] = (((x[i])[r]) + (sum * ((m[i])[j])));
			}
		}
	}
	for (r = 0; r <= 3; r += 1) {
		for (i = 3; i >= 0; i += -1) {
			for (j = (i + 1); j <= 3; j += 1) {
				(x[i])[r] = (((x[i])[r]) - (((x[j])[r]) * ((m[i])[j])));
			}
			(x[i])[r] = (((x[i])[r]) / ((d[i])[r]));
		}
	}
	for (i = 0; i <= 3; i += 1) {
		for (j = 0; j <= 3; j += 1) {
			rcvr[(i * 4) + j] = (((float) ((x[i])[j])));
		}
	}
}


/*	Primitive. Load the given index array into the receiver.
	NOTE: dstStart is a zero-based index. */

EXPORT(int) Squeak3D_b3dLoadIndexArray(void) {
    int idx;
    int dstStart;
    int maxValue;
    int srcArray;
    int dstArray;
    int count;
    int vtxOffset;
    int i;
    int *srcPtr;
    int dstSize;
    int *dstPtr;

	vtxOffset = interpreterProxy->stackIntegerValue(0);
	maxValue = interpreterProxy->stackIntegerValue(1);
	count = interpreterProxy->stackIntegerValue(2);
	srcArray = interpreterProxy->stackObjectValue(3);
	dstStart = interpreterProxy->stackIntegerValue(4);
	dstArray = interpreterProxy->stackObjectValue(5);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!(interpreterProxy->isWords(srcArray))) {
		return interpreterProxy->primitiveFail();
	}
	if ((interpreterProxy->slotSizeOf(srcArray)) < count) {
		return interpreterProxy->primitiveFail();
	}

	/* Check dstArray */

	srcPtr = ((int*) (interpreterProxy->firstIndexableField(srcArray)));

	/* Check if there is enough room left in dstArray */

	dstSize = interpreterProxy->slotSizeOf(dstArray);
	if ((dstStart + count) > dstSize) {
		return interpreterProxy->primitiveFail();
	}

	/* Do the actual work */

	dstPtr = ((int *) (interpreterProxy->firstIndexableField(dstArray)));
	for (i = 0; i <= (count - 1); i += 1) {
		idx = srcPtr[i];
		if ((idx < 1) || (idx > maxValue)) {
			return interpreterProxy->primitiveFail();
		}
		dstPtr[dstStart + i] = (idx + vtxOffset);
	}
	interpreterProxy->pop(7);
	interpreterProxy->pushInteger(count);
}


/*	Primitive. Load the data into the given vertex buffer.
	NOTE: dstStart is a zero-based index. */

EXPORT(int) Squeak3D_b3dLoadVertexBuffer(void) {
    int *colorPtr;
    int dstStart;
    int *vtxPtr;
    int count;
    int *texPtr;
    int *normalPtr;
    int *defaultNormal;
    int *defaultVtx;
    int i;
    int *defaultColor;
    int *pVtx;
    int *defaultTexCoords;
    int *dstPtr;

	defaultVtx = stackPrimitiveVertex(0);
	count = interpreterProxy->stackIntegerValue(1);
	texPtr = vbLoadArraysize(interpreterProxy->stackObjectValue(2), 2 * count);
	colorPtr = vbLoadArraysize(interpreterProxy->stackObjectValue(3), count);
	normalPtr = vbLoadArraysize(interpreterProxy->stackObjectValue(4), 3 * count);
	vtxPtr = vbLoadArraysize(interpreterProxy->stackObjectValue(5), 3 * count);
	dstStart = interpreterProxy->stackIntegerValue(6);

	/* Check for all problems above */

	dstPtr = stackPrimitiveVertexArrayofSize(7, dstStart + count);
	if ((dstPtr == null) || ((defaultVtx == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	if (normalPtr == null) {
		defaultNormal = defaultVtx + 3;
	} else {
		defaultNormal = normalPtr;
	}
	if (texPtr == null) {
		defaultTexCoords = defaultVtx + 6;
	} else {
		defaultTexCoords = texPtr;
	}
	if (colorPtr == null) {
		defaultColor = defaultVtx + 12;
	} else {
		defaultColor = colorPtr;
	}
	pVtx = dstPtr + (dstStart * 16);
	for (i = 0; i <= (count - 1); i += 1) {
		pVtx[0] = (vtxPtr[0]);
		pVtx[1] = (vtxPtr[1]);
		pVtx[2] = (vtxPtr[2]);
		pVtx[3] = (defaultNormal[0]);
		pVtx[4] = (defaultNormal[1]);
		pVtx[5] = (defaultNormal[2]);
		pVtx[12] = (defaultColor[0]);
		pVtx[6] = (defaultTexCoords[0]);
		pVtx[7] = (defaultTexCoords[1]);
		pVtx += 16;
		vtxPtr += 3;
		if (!(normalPtr == null)) {
			defaultNormal += 3;
		}
		if (!(colorPtr == null)) {
			defaultColor += 1;
		}
		if (!(texPtr == null)) {
			defaultTexCoords += 2;
		}
	}
	interpreterProxy->pop(9);
	interpreterProxy->pushInteger(count);
}


/*	Primitive. Determine the bounds for all vertices in the vertex buffer. */

EXPORT(int) Squeak3D_b3dMapVertexBuffer(void) {
    int boxArray;
    void *vtxArray;
    int vtxCount;

	if (!((interpreterProxy->methodArgumentCount()) == 3)) {
		return interpreterProxy->primitiveFail();
	}
	boxArray = interpreterProxy->stackObjectValue(0);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!(((interpreterProxy->fetchClassOf(boxArray)) == (interpreterProxy->classArray())) && ((interpreterProxy->slotSizeOf(boxArray)) == 4))) {
		return interpreterProxy->primitiveFail();
	}
	vtxCount = interpreterProxy->stackIntegerValue(1);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxArray = stackPrimitiveVertexArrayofSize(2, vtxCount);
	if ((vtxArray == null) || (interpreterProxy->failed())) {
		return interpreterProxy->primitiveFail();
	}
	mapVBofSizeinto(vtxArray, vtxCount, boxArray);
	if (!(interpreterProxy->failed())) {
		interpreterProxy->pop(3);
	}
}


/*	Primitive. Return the next clipped triangle from the vertex buffer and return its index. */

EXPORT(int) Squeak3D_b3dPrimitiveNextClippedTriangle(void) {
    int idx2;
    int idx3;
    int triMask;
    int *idxArray;
    int i;
    int idxCount;
    int *vtxArray;
    int vtxCount;
    int firstIndex;
    int idx1;

	if (!((interpreterProxy->methodArgumentCount()) == 5)) {
		return interpreterProxy->primitiveFail();
	}
	idxCount = interpreterProxy->stackIntegerValue(0);
	vtxCount = interpreterProxy->stackIntegerValue(2);
	firstIndex = interpreterProxy->stackIntegerValue(4);
	if (interpreterProxy->failed()) {
		return null;
	}
	vtxArray = stackPrimitiveVertexArrayofSize(3, vtxCount);
	idxArray = stackPrimitiveIndexArrayofSizevalidateforVertexSize(1, idxCount, 1, vtxCount);
	if ((vtxArray == null) || ((idxArray == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	idxArray -= 1;
	vtxArray -= 16;
	for (i = firstIndex; i <= idxCount; i += 3) {
		idx1 = idxArray[i];
		idx2 = idxArray[i + 1];
		idx3 = idxArray[i + 2];
		if (!((idx1 == 0) || ((idx2 == 0) || (idx3 == 0)))) {

			/* Check if tri is completely inside */

			triMask = (vtxArray[(idx1 * 16) + 13]) & ((vtxArray[(idx2 * 16) + 13]) & (vtxArray[(idx3 * 16) + 13]));
			if (!((1365 & triMask) == 1365)) {
				if (triMask & 2730) {
					idxArray[i] = 0;
					idxArray[i + 1] = 0;
					idxArray[i + 2] = 0;
				} else {
					interpreterProxy->pop(6);
					interpreterProxy->pushInteger(i);
					return null;
				}
			}
		}
	}
	interpreterProxy->pop(6);
	interpreterProxy->pushInteger(0);
}


/*	Primitive. Return the minimal number of words needed for a primitive object. */

EXPORT(int) Squeak3D_b3dPrimitiveObjectSize(void) {
    int objSize;

	objSize = (((int) (sizeof(B3DPrimitiveObject) + sizeof(B3DPrimitiveVertex)) >> 2)) + 1;
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(objSize);
}


/*	Primitive. Return the minimal number of words needed for a primitive object. */

EXPORT(int) Squeak3D_b3dPrimitiveTextureSize(void) {
    int objSize;

	objSize = (((int) (sizeof(B3DTexture)) >> 2)) + 1;
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(objSize);
}


/*	Primitive. Return the version of the rasterizer. */

EXPORT(int) Squeak3D_b3dRasterizerVersion(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(1);
}


/*	Primitive. Shade all the vertices in the vertex buffer using the given array of primitive light sources. Return true on success. */

EXPORT(int) Squeak3D_b3dShadeVertexBuffer(void) {
    int lightArray;
    int lightCount;
    int i;
    float *vtxArray;
    int j;
    int vtxCount;
    int g;
    int r;
    int b;
    int a;
    int lightOop;
    int rgba;

	vbFlags = interpreterProxy->stackIntegerValue(0);
	primMaterial = stackMaterialValue(1);
	lightArray = stackLightArrayValue(2);
	vtxCount = interpreterProxy->stackIntegerValue(3);
	vtxArray = stackPrimitiveVertexArrayofSize(4, vtxCount);
	if ((vtxArray == null) || ((primMaterial == null) || (interpreterProxy->failed()))) {
		return interpreterProxy->primitiveFail();
	}
	litVertex = vtxArray;

	/* Go over all vertices */

	lightCount = interpreterProxy->slotSizeOf(lightArray);
	for (i = 1; i <= vtxCount; i += 1) {
		/* begin loadPrimitiveVertex */
		rgba = (((int*) litVertex))[12];
		vtxInColor[2] = ((rgba & 255) * (1.0 / 255.0));
		rgba = ((unsigned) rgba) >> 8;
		vtxInColor[1] = ((rgba & 255) * (1.0 / 255.0));
		rgba = ((unsigned) rgba) >> 8;
		vtxInColor[0] = ((rgba & 255) * (1.0 / 255.0));
		rgba = ((unsigned) rgba) >> 8;
		vtxInColor[3] = ((rgba & 255) * (1.0 / 255.0));
		if (vbFlags & 8) {
			vtxOutColor[0] = ((vtxInColor[0]) + (primMaterial[12]));
			vtxOutColor[1] = ((vtxInColor[1]) + (primMaterial[13]));
			vtxOutColor[2] = ((vtxInColor[2]) + (primMaterial[14]));
			vtxOutColor[3] = ((vtxInColor[3]) + (primMaterial[15]));
		} else {
			vtxOutColor[0] = (primMaterial[12]);
			vtxOutColor[1] = (primMaterial[13]);
			vtxOutColor[2] = (primMaterial[14]);
			vtxOutColor[3] = (primMaterial[15]);
		}
		for (j = 0; j <= (lightCount - 1); j += 1) {
			/* begin fetchLightSource:ofObject: */
			lightOop = interpreterProxy->fetchPointerofObject(j, lightArray);
			primLight = interpreterProxy->firstIndexableField(lightOop);
			/* begin loadPrimitiveLightSource */
			lightFlags = (((int*) primLight))[21];
			shadeVertex();
		}
		/* begin storePrimitiveVertex */
		r = ((int) ((vtxOutColor[0]) * 255) );
		r = (((((r < 255) ? r : 255)) < 0) ? 0 : (((r < 255) ? r : 255)));
		g = ((int) ((vtxOutColor[1]) * 255) );
		g = (((((g < 255) ? g : 255)) < 0) ? 0 : (((g < 255) ? g : 255)));
		b = ((int) ((vtxOutColor[2]) * 255) );
		b = (((((b < 255) ? b : 255)) < 0) ? 0 : (((b < 255) ? b : 255)));
		a = ((int) ((vtxOutColor[3]) * 255) );
		a = (((((a < 255) ? a : 255)) < 0) ? 0 : (((a < 255) ? a : 255)));
		(((int*) litVertex))[12] = (b + ((g + ((r + (a << 8)) << 8)) << 8));
		litVertex += 16;
	}
	interpreterProxy->pop(6);
	interpreterProxy->pushBool(1);
}


/*	Return the current shader version. */

EXPORT(int) Squeak3D_b3dShaderVersion(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(1);
}


/*	Primitive. Start the rasterizer. */

EXPORT(int) Squeak3D_b3dStartRasterizer(void) {
    int errCode;

	if (!((interpreterProxy->methodArgumentCount()) == 3)) {
		return interpreterProxy->primitiveFail();
	}
	if (!(loadRasterizerState(2))) {
		return interpreterProxy->primitiveFail();
	}
	loadTexturesFrom(0);
	if (interpreterProxy->failed()) {
		return null;
	}
	loadObjectsFrom(1);
	if (interpreterProxy->failed()) {
		return null;
	}
	errCode = b3dMainLoop(&state, B3D_NO_ERROR);
	storeObjectsInto(1);
	interpreterProxy->pop(4);
	interpreterProxy->pushInteger(errCode);
}


/*	Transform two matrices into the third */

EXPORT(int) Squeak3D_b3dTransformMatrixWithInto(void) {
    float *m3;
    float *m1;
    float *m2;

	m3 = stackMatrix(0);
	m2 = stackMatrix(1);
	m1 = stackMatrix(2);
	if (((m1 == null) || (m2 == null)) || (m3 == null)) {
		return interpreterProxy->primitiveFail();
	}
	if (m2 == m3) {
		return interpreterProxy->primitiveFail();
	}
	transformMatrixwithinto(m1, m2, m3);
	interpreterProxy->pop(3);
}


/*	Transform the normal of the given primitive vertex using the argument matrix and rescale the normal if necessary. */

EXPORT(int) Squeak3D_b3dTransformPrimitiveNormal(void) {
    float *pVertex;
    float *matrix;
    int rescale;

	rescale = interpreterProxy->stackValue(0);
	if (!(rescale == (interpreterProxy->nilObject()))) {
		rescale = interpreterProxy->booleanValueOf(rescale);
	}
	matrix = stackMatrix(1);
	pVertex = stackPrimitiveVertex(2);
	if ((matrix == null) || (pVertex == null)) {
		return interpreterProxy->primitiveFail();
	}
	if ((rescale != 1) && (rescale != 0)) {
		rescale = analyzeMatrix3x3Length(matrix);
	}
	transformPrimitiveNormalbyrescale(pVertex, matrix, rescale);
	interpreterProxy->pop(3);
}


/*	Transform the position of the given primitive vertex the given matrix
	and store the result back inplace. */

EXPORT(int) Squeak3D_b3dTransformPrimitivePosition(void) {
    float *pVertex;
    float *matrix;

	matrix = stackMatrix(0);
	pVertex = stackPrimitiveVertex(1);
	if ((matrix == null) || (pVertex == null)) {
		return interpreterProxy->primitiveFail();
	}
	transformPrimitivePositionby(pVertex, matrix);
	interpreterProxy->pop(2);
}


/*	Transform the position of the given primitive vertex the given matrix
	and store the result in homogenous coordinates at rasterPos. */

EXPORT(int) Squeak3D_b3dTransformPrimitiveRasterPosition(void) {
    float *pVertex;
    float *matrix;

	matrix = stackMatrix(0);
	pVertex = stackPrimitiveVertex(1);
	if ((matrix == null) || (pVertex == null)) {
		return interpreterProxy->primitiveFail();
	}
	transformPrimitiveRasterPositionby(pVertex, matrix);
	interpreterProxy->pop(2);
}


/*	Transform an entire vertex buffer using the supplied modelview and projection matrix. */

EXPORT(int) Squeak3D_b3dTransformVertexBuffer(void) {
    float *projectionMatrix;
    int flags;
    float *modelViewMatrix;
    int vtxCount;
    float *vtxArray;

	flags = interpreterProxy->stackIntegerValue(0);
	projectionMatrix = stackMatrix(1);
	modelViewMatrix = stackMatrix(2);
	vtxCount = interpreterProxy->stackIntegerValue(3);
	vtxArray = stackPrimitiveVertexArrayofSize(4, vtxCount);
	if (((projectionMatrix == null) || (modelViewMatrix == null)) || (vtxArray == null)) {
		return interpreterProxy->primitiveFail();
	}
	if (interpreterProxy->failed()) {
		return null;
	}
	transformVBcountbyandflags(vtxArray, vtxCount, modelViewMatrix, projectionMatrix, flags);
	interpreterProxy->pop(5);
}


/*	Return the current version of the transformer */

EXPORT(int) Squeak3D_b3dTransformerVersion(void) {
	interpreterProxy->pop(1);
	interpreterProxy->pushInteger(1);
}

static double backClipValueFromto(int last, int next) {
	return (((((float *) last))[10]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[10]) - ((((float *) last))[10])));
}

static double bottomClipValueFromto(int last, int next) {
	return (0.0 - (((((float *) last))[9]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[9]) - ((((float *) last))[9])));
}

static int clipPolygoncountwithmask(int *vtxArray, int vtxCount, int *tempVtxArray, int outMask) {
    int count;

	if (outMask == 2) {
		return clipPolygonLeftFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	if (outMask == 8) {
		return clipPolygonRightFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	if (outMask == 32) {
		return clipPolygonTopFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	if (outMask == 128) {
		return clipPolygonBottomFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	if (outMask == 512) {
		return clipPolygonFrontFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	if (outMask == 2048) {
		return clipPolygonBackFromtocount(tempVtxArray, vtxArray, vtxCount);
	}
	count = vtxCount;
	count = clipPolygonLeftFromtocount(vtxArray, tempVtxArray, count);
	if (count == 0) {
		return 0;
	}
	count = clipPolygonRightFromtocount(tempVtxArray, vtxArray, count);
	if (count == 0) {
		return 0;
	}
	count = clipPolygonTopFromtocount(vtxArray, tempVtxArray, count);
	if (count == 0) {
		return 0;
	}
	count = clipPolygonBottomFromtocount(tempVtxArray, vtxArray, count);
	if (count == 0) {
		return 0;
	}
	count = clipPolygonFrontFromtocount(vtxArray, tempVtxArray, count);
	if (count == 0) {
		return 0;
	}
	count = clipPolygonBackFromtocount(tempVtxArray, vtxArray, count);
	return count;
}

static int clipPolygonBackFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 1024;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 1024;
		if (!(inLast == inNext)) {
			t = (((((float *) last))[10]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[10]) - ((((float *) last))[10])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}

static int clipPolygonBottomFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 64;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 64;
		if (!(inLast == inNext)) {
			t = (0.0 - (((((float *) last))[9]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[9]) - ((((float *) last))[9])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}

static int clipPolygonFrontFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 256;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 256;
		if (!(inLast == inNext)) {
			t = (0.0 - (((((float *) last))[10]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[10]) - ((((float *) last))[10])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}

static int clipPolygonLeftFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 1;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 1;
		if (!(inLast == inNext)) {
			t = (0.0 - (((((float *) last))[8]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[8]) - ((((float *) last))[8])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}

static int clipPolygonRightFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 4;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 4;
		if (!(inLast == inNext)) {
			t = (((((float *) last))[8]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[8]) - ((((float *) last))[8])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}

static int clipPolygonTopFromtocount(int *buf1, int *buf2, int n) {
    int inNext;
    int inLast;
    int *last;
    int *next;
    int outIndex;
    int i;
    int j;
    double t;

	outIndex = 0;
	last = buf1 + (n * 16);
	next = buf1 + 16;
	inLast = (last[13]) & 16;
	for (i = 1; i <= n; i += 1) {
		inNext = (next[13]) & 16;
		if (!(inLast == inNext)) {
			t = (((((float *) last))[9]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[9]) - ((((float *) last))[9])));
			outIndex += 1;
			interpolateFromtoatinto(((float *) last), ((float *) next), t, ((float*) (buf2 + (outIndex * 16))));
		}
		if (inNext) {
			outIndex += 1;
			for (j = 0; j <= (16 - 1); j += 1) {
				buf2[(outIndex * 16) + j] = (next[j]);
			}
		}
		last = next;
		inLast = inNext;
		next += 16;
	}
	return outIndex;
}


/*	Compute the attenuation for the current light and vertex */

static int computeAttenuation(void) {
	lightScale = 1.0;
	if (lightFlags & 4) {
		lightScale = 1.0 / ((primLight[18]) + (l2vDistance * ((primLight[19]) + (l2vDistance * (primLight[20])))));
	}
}


/*	Compute the direction for the current light and vertex */

static int computeDirection(void) {
    double scale;

	if (lightFlags & 1) {
		l2vDirection[0] = ((litVertex[0]) - (primLight[12]));
		l2vDirection[1] = ((litVertex[1]) - (primLight[13]));
		l2vDirection[2] = ((litVertex[2]) - (primLight[14]));
		l2vDistance = (((l2vDirection[0]) * (l2vDirection[0])) + ((l2vDirection[1]) * (l2vDirection[1]))) + ((l2vDirection[2]) * (l2vDirection[2]));
		if (!((l2vDistance == 0.0) || (l2vDistance == 1.0))) {
			l2vDistance = sqrt(l2vDistance);
			scale = -1.0 / l2vDistance;
		}
		l2vDirection[0] = ((l2vDirection[0]) * scale);
		l2vDirection[1] = ((l2vDirection[1]) * scale);
		l2vDirection[2] = ((l2vDirection[2]) * scale);
	} else {
		if (lightFlags & 2) {
			l2vDirection[0] = (primLight[15]);
			l2vDirection[1] = (primLight[16]);
			l2vDirection[2] = (primLight[17]);
		}
	}
}


/*	Computes
		l2vSpecDir _ l2vSpecDir - vtx position safelyNormalized.
	 */

static int computeSpecularDirection(void) {
    double scale;

	scale = inverseLengthOfFloat(litVertex + 0);
	l2vSpecDir[0] = ((l2vSpecDir[0]) - ((litVertex[0]) * scale));
	l2vSpecDir[1] = ((l2vSpecDir[1]) - ((litVertex[1]) * scale));
	l2vSpecDir[2] = ((l2vSpecDir[2]) - ((litVertex[2]) * scale));
}


/*	Compute the spot factor for a spot light */

static double computeSpotFactor(void) {
    double deltaCos;
    double minCos;
    double cosAngle;

	cosAngle = dotProductOfFloatwithDouble(primLight + 15, l2vDirection);
	cosAngle = 0.0 - cosAngle;
	minCos = primLight[22];
	if (cosAngle < minCos) {
		return 0.0;
	}
	deltaCos = primLight[24];
	if (deltaCos <= 1.0e-5) {
		return 1.0;
	}
	cosAngle = (cosAngle - minCos) / deltaCos;
	return pow(cosAngle,(primLight[25]));
}

static int determineClipFlagscount(void *vtxArray, int count) {
    float *vtxPtr;
    double w;
    int fullMask;
    double x;
    double w2;
    double y;
    double z;
    int i;
    int flags;

	vtxPtr = ((float *) vtxArray);
	fullMask = 1365 + 2730;
	for (i = 1; i <= count; i += 1) {
		w = vtxPtr[11];
		w2 = 0.0 - w;
		flags = 0;
		x = vtxPtr[8];
		if (x >= w2) {
			flags = flags | 1;
		} else {
			flags = flags | 2;
		}
		if (x <= w) {
			flags = flags | 4;
		} else {
			flags = flags | 8;
		}
		y = vtxPtr[9];
		if (y >= w2) {
			flags = flags | 64;
		} else {
			flags = flags | 128;
		}
		if (y <= w) {
			flags = flags | 16;
		} else {
			flags = flags | 32;
		}
		z = vtxPtr[10];
		if (z >= w2) {
			flags = flags | 256;
		} else {
			flags = flags | 512;
		}
		if (z <= w) {
			flags = flags | 1024;
		} else {
			flags = flags | 2048;
		}
		fullMask = fullMask & flags;
		(((int *) vtxPtr))[13] = flags;
		vtxPtr += 16;
	}
	return fullMask;
}

static double dotProductOfFloatwithDouble(float * v1, double *v2) {
	return (((v1[0]) * (v2[0])) + ((v1[1]) * (v2[1]))) + ((v1[2]) * (v2[2]));
}


/*	Fetch the primitive light source from the given array.
	Note: No checks are done within here - that happened in stackLightArrayValue: */

static void* fetchLightSourceofObject(int index, int anArray) {
    int lightOop;

	lightOop = interpreterProxy->fetchPointerofObject(index, anArray);
	return interpreterProxy->firstIndexableField(lightOop);
}

static double frontClipValueFromto(int last, int next) {
	return (0.0 - (((((float *) last))[10]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[10]) - ((((float *) last))[10])));
}


/*	Note: This is hardcoded so it can be run from Squeak.
	The module name is used for validating a module *after*
	it is loaded to check if it does really contain the module
	we're thinking it contains. This is important! */

EXPORT(const char*) Squeak3D_getModuleName(void) {
	return moduleName;
}

EXPORT(int) Squeak3D_initialiseModule(void) {
	loadBBFn = interpreterProxy->ioLoadFunctionFrom("loadBitBltFrom", bbPluginName);
	copyBitsFn = interpreterProxy->ioLoadFunctionFrom("copyBitsFromtoat", bbPluginName);
	return (loadBBFn != 0) && (copyBitsFn != 0);
}


/*	Interpolate the primitive vertices last/next at the parameter t */

static int interpolateFromtoatinto(float *last, float *next, double t, float *out) {
    unsigned int rgbaNext;
    double w;
    unsigned int rgbaLast;
    double x;
    double w2;
    double y;
    double z;
    double delta;
    unsigned int nextValue;
    int flags;
    unsigned int newValue;
    unsigned int lastValue;

	delta = (next[8]) - (last[8]);
	x = (last[8]) + (delta * t);
	out[8] = (((float) x));
	delta = (next[9]) - (last[9]);
	y = (last[9]) + (delta * t);
	out[9] = (((float) y));
	delta = (next[10]) - (last[10]);
	z = (last[10]) + (delta * t);
	out[10] = (((float) z));
	delta = (next[11]) - (last[11]);
	w = (last[11]) + (delta * t);
	out[11] = (((float) w));
	w2 = 0.0 - w;
	flags = 0;
	if (x >= w2) {
		flags = flags | 1;
	} else {
		flags = flags | 2;
	}
	if (x <= w) {
		flags = flags | 4;
	} else {
		flags = flags | 8;
	}
	if (y >= w2) {
		flags = flags | 64;
	} else {
		flags = flags | 128;
	}
	if (y <= w) {
		flags = flags | 16;
	} else {
		flags = flags | 32;
	}
	if (z >= w2) {
		flags = flags | 256;
	} else {
		flags = flags | 512;
	}
	if (z <= w) {
		flags = flags | 1024;
	} else {
		flags = flags | 2048;
	}
	(((int *) out))[13] = flags;
	rgbaLast = (((unsigned int *) last))[12];
	lastValue = rgbaLast & 255;
	rgbaLast = ((unsigned) rgbaLast) >> 8;
	rgbaNext = (((unsigned int *) next))[12];
	nextValue = rgbaNext & 255;
	rgbaNext = ((unsigned) rgbaNext) >> 8;
	delta = (((int) (nextValue - lastValue))) * t;
	newValue = ((int) (lastValue + delta) );
	lastValue = rgbaLast & 255;
	rgbaLast = ((unsigned) rgbaLast) >> 8;
	nextValue = rgbaNext & 255;
	rgbaNext = ((unsigned) rgbaNext) >> 8;
	delta = (((int) (nextValue - lastValue))) * t;
	newValue += (((int) (lastValue + delta) )) << 8;
	lastValue = rgbaLast & 255;
	rgbaLast = ((unsigned) rgbaLast) >> 8;
	nextValue = rgbaNext & 255;
	rgbaNext = ((unsigned) rgbaNext) >> 8;
	delta = (((int) (nextValue - lastValue))) * t;
	newValue += (((int) (lastValue + delta) )) << 16;
	lastValue = rgbaLast & 255;
	nextValue = rgbaNext & 255;
	delta = (((int) (nextValue - lastValue))) * t;
	newValue += (((int) (lastValue + delta) )) << 24;
	(((unsigned int*) out))[12] = newValue;
	delta = (next[6]) - (last[6]);
	out[6] = (((float) ((last[6]) + (delta * t))));
	delta = (next[7]) - (last[7]);
	out[7] = (((float) ((last[7]) + (delta * t))));
}

static double inverseLengthOfDouble(double * aVector) {
    double scale;

	scale = (((aVector[0]) * (aVector[0])) + ((aVector[1]) * (aVector[1]))) + ((aVector[2]) * (aVector[2]));
	if ((scale == 0.0) || (scale == 1.0)) {
		return scale;
	}
	return 1.0 / (sqrt(scale));
}

static double inverseLengthOfFloat(float * aVector) {
    double scale;

	scale = (((aVector[0]) * (aVector[0])) + ((aVector[1]) * (aVector[1]))) + ((aVector[2]) * (aVector[2]));
	if ((scale == 0.0) || (scale == 1.0)) {
		return scale;
	}
	return 1.0 / (sqrt(scale));
}

static double leftClipValueFromto(int last, int next) {
	return (0.0 - (((((float *) last))[8]) + ((((float *) last))[11]))) / ((((((float *) next))[11]) - ((((float *) last))[11])) + (((((float *) next))[8]) - ((((float *) last))[8])));
}

static int loadObjectsFrom(int stackIndex) {
    int arrayOop;
    int objOop;
    int arraySize;
    int i;
    B3DPrimitiveObject **objArray;
    B3DPrimitiveObject *objPtr;

	arrayOop = interpreterProxy->stackObjectValue(stackIndex);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!((interpreterProxy->fetchClassOf(arrayOop)) == (interpreterProxy->classArray()))) {
		return interpreterProxy->primitiveFail();
	}
	arraySize = interpreterProxy->slotSizeOf(arrayOop);
	if (arraySize > (state.nObjects)) {
		return interpreterProxy->primitiveFail();
	}
	objArray = state.objects;
	for (i = 0; i <= (arraySize - 1); i += 1) {
		objOop = interpreterProxy->fetchPointerofObject(i, arrayOop);
		if (((objOop & 1)) || (!(interpreterProxy->isWords(objOop)))) {
			return interpreterProxy->primitiveFail();
		}
		objPtr = ((B3DPrimitiveObject*) (interpreterProxy->firstIndexableField(objOop)));
		if (objPtr->magic != B3D_PRIMITIVE_OBJECT_MAGIC) {
			return interpreterProxy->primitiveFail();
		}
		objPtr->__oop__ = objOop;
		objArray[i] = objPtr;
	}
}

static int loadPrimitiveLightSource(void) {
	lightFlags = (((int*) primLight))[21];
}


/*	Load the necessary values from the current primitive vertex */

static int loadPrimitiveVertex(void) {
    int rgba;

	rgba = (((int*) litVertex))[12];
	vtxInColor[2] = ((rgba & 255) * (1.0 / 255.0));
	rgba = ((unsigned) rgba) >> 8;
	vtxInColor[1] = ((rgba & 255) * (1.0 / 255.0));
	rgba = ((unsigned) rgba) >> 8;
	vtxInColor[0] = ((rgba & 255) * (1.0 / 255.0));
	rgba = ((unsigned) rgba) >> 8;
	vtxInColor[3] = ((rgba & 255) * (1.0 / 255.0));
}


/*	Load the rasterizer state from the given stack index. */

static int loadRasterizerState(int stackIndex) {
    int obj;
    int stateOop;
    void *objPtr;
    int objLen;

	if ((copyBitsFn == 0) || (loadBBFn == 0)) {
		if (!(Squeak3D_initialiseModule())) {
			return 0;
		}
	}
	stateOop = interpreterProxy->stackObjectValue(stackIndex);
	if (interpreterProxy->failed()) {
		return 0;
	}
	if (!((interpreterProxy->isPointers(stateOop)) && ((interpreterProxy->slotSizeOf(stateOop)) >= 10))) {
		return 0;
	}
	obj = interpreterProxy->fetchPointerofObject(0, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.faceAlloc = objPtr;
	obj = interpreterProxy->fetchPointerofObject(1, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.edgeAlloc = objPtr;
	obj = interpreterProxy->fetchPointerofObject(2, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.attrAlloc = objPtr;
	obj = interpreterProxy->fetchPointerofObject(3, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.aet = objPtr;
	obj = interpreterProxy->fetchPointerofObject(4, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.addedEdges = objPtr;
	obj = interpreterProxy->fetchPointerofObject(5, stateOop);
	if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
		return 0;
	}
	objPtr = interpreterProxy->firstIndexableField(obj);
	state.fillList = objPtr;
	obj = interpreterProxy->fetchPointerofObject(6, stateOop);
	if (obj == (interpreterProxy->nilObject())) {
		state.nObjects = 0;
		state.objects = NULL;
	} else {
		if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
			return 0;
		}
		objLen = interpreterProxy->slotSizeOf(obj);
		objPtr = interpreterProxy->firstIndexableField(obj);
		state.objects = (B3DPrimitiveObject **)objPtr;
		state.nObjects = objLen;
	}
	obj = interpreterProxy->fetchPointerofObject(7, stateOop);
	if (obj == (interpreterProxy->nilObject())) {
		state.nTextures = 0;
		state.textures = NULL;
	} else {
		if (((obj & 1)) || (!(interpreterProxy->isWords(obj)))) {
			return 0;
		}
		objLen = interpreterProxy->byteSizeOf(obj);
		objPtr = interpreterProxy->firstIndexableField(obj);
		state.textures = (B3DTexture *)objPtr;
		state.nTextures = objLen / sizeof(B3DTexture);
	}
	obj = interpreterProxy->fetchPointerofObject(8, stateOop);
	if (obj == (interpreterProxy->nilObject())) {
		state.spanSize = 0;
		state.spanBuffer = NULL;
	} else {
		if (!((interpreterProxy->fetchClassOf(obj)) == (interpreterProxy->classBitmap()))) {
			return 0;
		}
		objLen = interpreterProxy->slotSizeOf(obj);
		objPtr = interpreterProxy->firstIndexableField(obj);
		state.spanBuffer = (unsigned int *)objPtr;
		state.spanSize = objLen;
	}
	obj = interpreterProxy->fetchPointerofObject(9, stateOop);
	if (obj == (interpreterProxy->nilObject())) {
		state.spanDrawer = NULL;
	} else {
		if (!(((int (*) (int))loadBBFn)(obj))) {
			return 0;
		}
		state.spanDrawer = (b3dDrawBufferFunction) copyBitsFn;
	}
	return !(interpreterProxy->failed());
}


/*	Note: This still uses the old-style textures */

static int loadTextureinto(int textureOop, B3DTexture *destPtr) {
    int formBits;
    void *bitsPtr;
    int texInterpolate;
    int formWidth;
    int formHeight;
    int formDepth;
    int texWrap;
    int texEnvMode;
    int form;

	form = textureOop;
	if (!(interpreterProxy->isPointers(form))) {
		return 0;
	}
	if ((interpreterProxy->slotSizeOf(form)) < 8) {
		return 0;
	}
	formBits = interpreterProxy->fetchPointerofObject(0, form);
	formWidth = interpreterProxy->fetchIntegerofObject(1, form);
	formHeight = interpreterProxy->fetchIntegerofObject(2, form);
	formDepth = interpreterProxy->fetchIntegerofObject(3, form);
	texWrap = interpreterProxy->booleanValueOf(interpreterProxy->fetchPointerofObject(5, form));
	texInterpolate = interpreterProxy->booleanValueOf(interpreterProxy->fetchPointerofObject(6, form));
	texEnvMode = interpreterProxy->fetchIntegerofObject(7, form);
	if (interpreterProxy->failed()) {
		return 0;
	}
	if ((formWidth < 1) || ((formHeight < 1) || (formDepth != 32))) {
		return 0;
	}
	if (!((interpreterProxy->fetchClassOf(formBits)) == (interpreterProxy->classBitmap()))) {
		return 0;
	}
	if (!((interpreterProxy->byteSizeOf(formBits)) == ((formWidth * formHeight) * 4))) {
		return 0;
	}
	if ((texEnvMode < 0) || (texEnvMode > 1)) {
		return 0;
	}

	/* Set the texture parameters */

	bitsPtr = interpreterProxy->firstIndexableField(formBits);
	return b3dLoadTexture(destPtr, formWidth, formHeight, formDepth, (unsigned int*) bitsPtr, 0, NULL) == B3D_NO_ERROR;
}

static int loadTexturesFrom(int stackIndex) {
    int n;
    int arrayOop;
    int textureOop;
    int i;
    B3DTexture *destPtr;

	arrayOop = interpreterProxy->stackObjectValue(stackIndex);
	if (!((interpreterProxy->fetchClassOf(arrayOop)) == (interpreterProxy->classArray()))) {
		return interpreterProxy->primitiveFail();
	}
	n = interpreterProxy->slotSizeOf(arrayOop);
	n = ((n < (state.nTextures)) ? n : (state.nTextures));
	for (i = 0; i <= (n - 1); i += 1) {
		destPtr = state.textures + i;
		textureOop = interpreterProxy->fetchPointerofObject(i, arrayOop);
		if (!(loadTextureinto(textureOop, destPtr))) {
			return interpreterProxy->primitiveFail();
		}
	}
	return 0;
}


/*	Load the viewport from the given stack index */

static int loadViewportFrom(int stackIndex) {
    int x0;
    int y0;
    int x1;
    int oop;
    int y1;
    int p1;
    int p2;

	oop = interpreterProxy->stackObjectValue(stackIndex);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!(interpreterProxy->isPointers(oop))) {
		return interpreterProxy->primitiveFail();
	}
	if ((interpreterProxy->slotSizeOf(oop)) < 2) {
		return interpreterProxy->primitiveFail();
	}
	p1 = interpreterProxy->fetchPointerofObject(0, oop);
	p2 = interpreterProxy->fetchPointerofObject(1, oop);
	if (!((interpreterProxy->fetchClassOf(p1)) == (interpreterProxy->classPoint()))) {
		return interpreterProxy->primitiveFail();
	}
	if (!((interpreterProxy->fetchClassOf(p2)) == (interpreterProxy->classPoint()))) {
		return interpreterProxy->primitiveFail();
	}
	x0 = interpreterProxy->fetchIntegerofObject(0, p1);
	y0 = interpreterProxy->fetchIntegerofObject(1, p1);
	x1 = interpreterProxy->fetchIntegerofObject(0, p2);
	y1 = interpreterProxy->fetchIntegerofObject(1, p2);
	if (interpreterProxy->failed()) {
		return null;
	}
	viewport.x0 = x0;
	viewport.y0 = y0;
	viewport.x1 = x1;
	viewport.y1 = y1;
	return 0;
}

static int mapVBofSizeinto(void *vtxArray, int vtxCount, int boxArray) {
    float *vtxPtr;
    double w;
    double x;
    double right;
    int floatOop;
    double y;
    double left;
    int i;
    double top;
    int oop;
    int flags;
    double bottom;

	vtxPtr = ((float *) vtxArray);
	for (i = 1; i <= vtxCount; i += 1) {
		flags = (((int *) vtxPtr))[13];
		w = vtxPtr[11];
		if (!(w == 0.0)) {
			w = 1.0 / w;
		}
		if ((flags & 2) != 0) {
			x = -1.0;
		} else {
			if ((flags & 8) != 0) {
				x = 1.0;
			} else {
				x = (vtxPtr[8]) * w;
			}
		}
		if ((flags & 32) != 0) {
			y = -1.0;
		} else {
			if ((flags & 128) != 0) {
				y = 1.0;
			} else {
				y = (vtxPtr[9]) * w;
			}
		}
		if (i == 1) {
			left = right = x;
			top = bottom = y;
		}
		if (x < left) {
			left = x;
		}
		if (x > right) {
			right = x;
		}
		if (y < top) {
			top = y;
		}
		if (y > bottom) {
			bottom = y;
		}
		vtxPtr += 16;
	}
	oop = boxArray;
	interpreterProxy->pushRemappableOop(oop);
	floatOop = interpreterProxy->floatObjectOf(left);
	oop = interpreterProxy->popRemappableOop();
	interpreterProxy->storePointerofObjectwithValue(0, oop, floatOop);
	interpreterProxy->pushRemappableOop(oop);
	floatOop = interpreterProxy->floatObjectOf(top);
	oop = interpreterProxy->popRemappableOop();
	interpreterProxy->storePointerofObjectwithValue(1, oop, floatOop);
	interpreterProxy->pushRemappableOop(oop);
	floatOop = interpreterProxy->floatObjectOf(right);
	oop = interpreterProxy->popRemappableOop();
	interpreterProxy->storePointerofObjectwithValue(2, oop, floatOop);
	interpreterProxy->pushRemappableOop(oop);
	floatOop = interpreterProxy->floatObjectOf(bottom);
	oop = interpreterProxy->popRemappableOop();
	interpreterProxy->storePointerofObjectwithValue(3, oop, floatOop);
}


/*	The module with the given name was just unloaded.
	Make sure we have no dangling references. */

EXPORT(int) Squeak3D_moduleUnloaded(char * aModuleName) {
	if ((strcmp(aModuleName, bbPluginName)) == 0) {
		loadBBFn = 0;
		copyBitsFn = 0;
	}
}


/*	Primitive. Set the BitBlt plugin to use. */

EXPORT(int) Squeak3D_primitiveSetBitBltPlugin(void) {
    int length;
    char *ptr;
    int i;
    int pluginName;
    int needReload;


	/* Must be string to work */

	pluginName = interpreterProxy->stackValue(0);
	if (!(interpreterProxy->isBytes(pluginName))) {
		return interpreterProxy->primitiveFail();
	}
	length = interpreterProxy->byteSizeOf(pluginName);
	if (length >= 256) {
		return interpreterProxy->primitiveFail();
	}
	ptr = interpreterProxy->firstIndexableField(pluginName);
	needReload = 0;
	for (i = 0; i <= (length - 1); i += 1) {
		if (!((bbPluginName[i]) == (ptr[i]))) {
			bbPluginName[i] = (ptr[i]);
			needReload = 1;
		}
	}
	if (!((bbPluginName[length]) == 0)) {
		bbPluginName[length] = 0;
		needReload = 1;
	}
	if (needReload) {
		if (!(Squeak3D_initialiseModule())) {
			return interpreterProxy->primitiveFail();
		}
	}
	interpreterProxy->pop(1);
}

static double processIndexedofSizeidxArrayidxSize(float *vtxArray, int vtxSize, int *idxArray, int idxSize) {
    double zValue;
    float *vtxPtr;
    double minZ;
    int i;
    double wValue;
    int index;

	minZ = 10.0;
	for (i = 1; i <= idxSize; i += 1) {
		index = idxArray[i];
		if (index > 0) {
			vtxPtr = vtxArray + ((index - 1) * 16);
			zValue = vtxPtr[10];
			wValue = vtxPtr[11];
			if (!(wValue == 0.0)) {
				zValue = zValue / wValue;
			}
			if (zValue < minZ) {
				minZ = zValue;
			}
		}
	}
	return minZ;
}

static int processIndexedIDXofSizeidxArrayidxSize(float *vtxArray, int vtxSize, int *idxArray, int idxSize) {
    double zValue;
    float *vtxPtr;
    double minZ;
    int minIndex;
    int i;
    double wValue;
    int index;

	minZ = 10.0;
	minIndex = 0;
	for (i = 1; i <= idxSize; i += 1) {
		index = idxArray[i];
		if (index > 0) {
			vtxPtr = vtxArray + ((index - 1) * 16);
			zValue = vtxPtr[10];
			wValue = vtxPtr[11];
			if (!(wValue == 0.0)) {
				zValue = zValue / wValue;
			}
			if ((minIndex == 0) || (zValue < minZ)) {
				minIndex = i;
				minZ = zValue;
			}
		}
	}
	return minIndex;
}

static double processNonIndexedofSize(float *vtxArray, int vtxSize) {
    double zValue;
    float *vtxPtr;
    double minZ;
    int i;
    double wValue;

	minZ = 10.0;
	vtxPtr = vtxArray;
	for (i = 1; i <= vtxSize; i += 1) {
		zValue = vtxPtr[10];
		wValue = vtxPtr[11];
		if (!(wValue == 0.0)) {
			zValue = zValue / wValue;
		}
		if (zValue < minZ) {
			minZ = zValue;
		}
	}
	return minZ;
}

static int processNonIndexedIDXofSize(float *vtxArray, int vtxSize) {
    double zValue;
    float *vtxPtr;
    double minZ;
    int minIndex;
    int i;
    double wValue;

	minZ = 10.0;
	minIndex = 0;
	vtxPtr = vtxArray;
	for (i = 1; i <= vtxSize; i += 1) {
		zValue = vtxPtr[10];
		wValue = vtxPtr[11];
		if (!(wValue == 0.0)) {
			zValue = zValue / wValue;
		}
		if ((minIndex == 0) || (zValue < minZ)) {
			minIndex = i;
			minZ = zValue;
		}
	}
	return minIndex;
}

static double rightClipValueFromto(int last, int next) {
	return (((((float *) last))[8]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[8]) - ((((float *) last))[8])));
}


/*	Note: This is coded so that is can be run from Squeak. */

EXPORT(int) Squeak3D_setInterpreter(struct VirtualMachine* anInterpreter) {
    int ok;

	interpreterProxy = anInterpreter;
	ok = interpreterProxy->majorVersion() == VM_PROXY_MAJOR;
	if (ok == 0) {
		return 0;
	}
	ok = interpreterProxy->minorVersion() >= VM_PROXY_MINOR;
	return ok;
}

static int shadeVertex(void) {
    double specularFactor;
    double cosAngle;

	computeDirection();
	/* begin computeAttenuation */
	lightScale = 1.0;
	if (lightFlags & 4) {
		lightScale = 1.0 / ((primLight[18]) + (l2vDistance * ((primLight[19]) + (l2vDistance * (primLight[20])))));
	}
	if (lightFlags & 8) {
		lightScale = lightScale * (computeSpotFactor());
	}
	if (lightScale > 0.001) {
		if (lightFlags & 256) {
			addPartfromtrackFlagscale(primLight + 0, primMaterial + 0, 1, lightScale);
		}
		if (lightFlags & 512) {

			/* For one-sided lighting negate cosAngle if necessary */

			cosAngle = dotProductOfFloatwithDouble(litVertex + 3, l2vDirection);
			if (((vbFlags & 64) == 0) && (cosAngle < 0.0)) {
				cosAngle = 0.0 - cosAngle;
			}
			if (cosAngle > 0.0) {
				addPartfromtrackFlagscale(primLight + 4, primMaterial + 4, 2, lightScale * cosAngle);
			}
		}
	}
	if ((lightFlags & 1024) && ((primMaterial[16]) > 0.0)) {
		l2vSpecDir[0] = (l2vDirection[0]);
		l2vSpecDir[1] = (l2vDirection[1]);
		l2vSpecDir[2] = (l2vDirection[2]);
		if (vbFlags & 128) {
			computeSpecularDirection();
		} else {
			l2vSpecDir[2] = ((l2vSpecDir[2]) - 1.0);
		}
		cosAngle = dotProductOfFloatwithDouble(litVertex + 3, l2vSpecDir);
		if (cosAngle > 0.0) {

			/* cosAngle should be somewhere between 0 and 1.
			If not, then the vertex normal was not normalized */

			cosAngle = cosAngle * (inverseLengthOfDouble(l2vSpecDir));
			if (cosAngle > 1.0) {
				specularFactor = pow(cosAngle,(primMaterial[16]));
			} else {
				if (cosAngle == 0.0) {
					specularFactor = 1.0;
				} else {
					specularFactor = pow(cosAngle,(primMaterial[16]));
				}
			}
			addPartfromtrackFlagscale(primLight + 8, primMaterial + 8, 4, specularFactor);
		}
	}
}


/*	Load an Array of B3DPrimitiveLights from the given stack index */

static int stackLightArrayValue(int stackIndex) {
    int array;
    int arraySize;
    int i;
    int oop;

	array = interpreterProxy->stackObjectValue(stackIndex);
	if (interpreterProxy->failed()) {
		return null;
	}
	if (!((interpreterProxy->fetchClassOf(array)) == (interpreterProxy->classArray()))) {
		return interpreterProxy->primitiveFail();
	}
	arraySize = interpreterProxy->slotSizeOf(array);
	for (i = 0; i <= (arraySize - 1); i += 1) {
		oop = interpreterProxy->fetchPointerofObject(i, array);
		if ((oop & 1)) {
			return interpreterProxy->primitiveFail();
		}
		if (!((interpreterProxy->isWords(oop)) && ((interpreterProxy->slotSizeOf(oop)) == 32))) {
			return interpreterProxy->primitiveFail();
		}
	}
	return array;
}


/*	Load a B3DMaterial from the given stack index */

static void * stackMaterialValue(int stackIndex) {
    int oop;

	oop = interpreterProxy->stackObjectValue(stackIndex);
	if (interpreterProxy->failed()) {
		return null;
	}
	if ((interpreterProxy->isWords(oop)) && ((interpreterProxy->slotSizeOf(oop)) == 17)) {
		return interpreterProxy->firstIndexableField(oop);
	}
	return null;
}


/*	Load a 4x4 transformation matrix from the interpreter stack.
	Return a pointer to the matrix data if successful, nil otherwise. */

static void* stackMatrix(int index) {
    int oop;

	oop = interpreterProxy->stackObjectValue(index);
	if (oop == null) {
		return null;
	}
	if ((interpreterProxy->isWords(oop)) && ((interpreterProxy->slotSizeOf(oop)) == 16)) {
		return interpreterProxy->firstIndexableField(oop);
	}
	return null;
}


/*	Load a primitive index array from the interpreter stack.
	If aBool is true then check that all the indexes are in the range (1,maxIndex).
	Return a pointer to the index data if successful, nil otherwise. */

static void* stackPrimitiveIndexArrayofSizevalidateforVertexSize(int stackIndex, int nItems, int aBool, int maxIndex) {
    int oopSize;
    int i;
    int oop;
    int *idxPtr;
    int index;

	oop = interpreterProxy->stackObjectValue(stackIndex);
	if (oop == null) {
		return null;
	}
	if (!(interpreterProxy->isWords(oop))) {
		return null;
	}
	oopSize = interpreterProxy->slotSizeOf(oop);
	if (oopSize < nItems) {
		return null;
	}
	idxPtr = ((int *) (interpreterProxy->firstIndexableField(oop)));
	if (aBool) {
		for (i = 0; i <= (nItems - 1); i += 1) {
			index = idxPtr[i];
			if ((index < 0) || (index > maxIndex)) {
				return null;
			}
		}
	}
	return idxPtr;
}


/*	Load a primitive vertex from the interpreter stack.
	Return a pointer to the vertex data if successful, nil otherwise. */

static void* stackPrimitiveVertex(int index) {
    int oop;

	oop = interpreterProxy->stackObjectValue(index);
	if (oop == null) {
		return null;
	}
	if ((interpreterProxy->isWords(oop)) && ((interpreterProxy->slotSizeOf(oop)) == 16)) {
		return interpreterProxy->firstIndexableField(oop);
	}
	return null;
}


/*	Load a primitive vertex array from the interpreter stack.
	Return a pointer to the vertex data if successful, nil otherwise. */

static void* stackPrimitiveVertexArrayofSize(int index, int nItems) {
    int oop;
    int oopSize;

	oop = interpreterProxy->stackObjectValue(index);
	if (oop == null) {
		return null;
	}
	if (interpreterProxy->isWords(oop)) {
		oopSize = interpreterProxy->slotSizeOf(oop);
		if (((oopSize >= nItems) * 16) && ((oopSize % 16) == 0)) {
			return interpreterProxy->firstIndexableField(oop);
		}
	}
	return null;
}

static int storeObjectsInto(int stackIndex) {
    int arrayOop;
    int objOop;
    int arraySize;
    int i;

	arrayOop = interpreterProxy->stackObjectValue(stackIndex);
	arraySize = state.nObjects;
	for (i = 0; i <= (arraySize - 1); i += 1) {
		objOop = state.objects[i]->__oop__;
		interpreterProxy->storePointerofObjectwithValue(i, arrayOop, objOop);
	}
}


/*	Store the computed output color back into the current primitive vertex.
	Clamp the r,g,b,a part to be in the range 0-255. */

static int storePrimitiveVertex(void) {
    int g;
    int r;
    int b;
    int a;

	r = ((int) ((vtxOutColor[0]) * 255) );
	r = (((((r < 255) ? r : 255)) < 0) ? 0 : (((r < 255) ? r : 255)));
	g = ((int) ((vtxOutColor[1]) * 255) );
	g = (((((g < 255) ? g : 255)) < 0) ? 0 : (((g < 255) ? g : 255)));
	b = ((int) ((vtxOutColor[2]) * 255) );
	b = (((((b < 255) ? b : 255)) < 0) ? 0 : (((b < 255) ? b : 255)));
	a = ((int) ((vtxOutColor[3]) * 255) );

	/* The following is equal to b + (g << 8) + (r << 16) + (a << 24) */

	a = (((((a < 255) ? a : 255)) < 0) ? 0 : (((a < 255) ? a : 255)));
	(((int*) litVertex))[12] = (b + ((g + ((r + (a << 8)) << 8)) << 8));
}

static double topClipValueFromto(int last, int next) {
	return (((((float *) last))[9]) - ((((float *) last))[11])) / ((((((float *) next))[11]) - ((((float *) last))[11])) - (((((float *) next))[9]) - ((((float *) last))[9])));
}


/*	Transform src with arg into dst.
	It is allowed that src == dst but not arg == dst */

static int transformMatrixwithinto(float *src, float *arg, float *dst) {
    float *m1;
    float c1;
    float c2;
    float *m2;
    float c3;
    float *m3;
    int i;
    float c4;

	m1 = ((float *) src);
	m2 = ((float *) arg);
	m3 = ((float *) dst);
	for (i = 0; i <= 3; i += 1) {
		c1 = ((((m1[0]) * (m2[0])) + ((m1[1]) * (m2[4]))) + ((m1[2]) * (m2[8]))) + ((m1[3]) * (m2[12]));
		c2 = ((((m1[0]) * (m2[1])) + ((m1[1]) * (m2[5]))) + ((m1[2]) * (m2[9]))) + ((m1[3]) * (m2[13]));
		c3 = ((((m1[0]) * (m2[2])) + ((m1[1]) * (m2[6]))) + ((m1[2]) * (m2[10]))) + ((m1[3]) * (m2[14]));

		/* Store result */

		c4 = ((((m1[0]) * (m2[3])) + ((m1[1]) * (m2[7]))) + ((m1[2]) * (m2[11]))) + ((m1[3]) * (m2[15]));
		m3[0] = c1;
		m3[1] = c2;
		m3[2] = c3;
		m3[3] = c4;
		m1 += 4;
		m3 += 4;
	}
}


/*	Transform the normal of the given primitive vertex */

static int transformPrimitiveNormalbyrescale(float *pVertex, float *matrix, int rescale) {
    double x;
    double rx;
    double y;
    double ry;
    double z;
    double rz;
    double dot;

	x = pVertex[3];
	y = pVertex[4];
	z = pVertex[5];
	rx = ((x * (matrix[0])) + (y * (matrix[1]))) + (z * (matrix[2]));
	ry = ((x * (matrix[4])) + (y * (matrix[5]))) + (z * (matrix[6]));
	rz = ((x * (matrix[8])) + (y * (matrix[9]))) + (z * (matrix[10]));
	if (rescale) {
		dot = ((rx * rx) + (ry * ry)) + (rz * rz);
		if (dot < 1.0e-20) {
			rx = ry = rz = 0.0;
		} else {
			if (!(dot == 1.0)) {
				dot = 1.0 / (sqrt(dot));
				rx = rx * dot;
				ry = ry * dot;
				rz = rz * dot;
			}
		}
	}
	pVertex[3] = (((float) rx));
	pVertex[4] = (((float) ry));
	pVertex[5] = (((float) rz));
}


/*	Transform the normal of the given primitive vertex */

static int transformPrimitivePositionby(float *pVertex, float *matrix) {
    double rw;
    double x;
    double rx;
    double y;
    double ry;
    double z;
    double rz;

	x = pVertex[0];
	y = pVertex[1];
	z = pVertex[2];
	rx = (((x * (matrix[0])) + (y * (matrix[1]))) + (z * (matrix[2]))) + (matrix[3]);
	ry = (((x * (matrix[4])) + (y * (matrix[5]))) + (z * (matrix[6]))) + (matrix[7]);
	rz = (((x * (matrix[8])) + (y * (matrix[9]))) + (z * (matrix[10]))) + (matrix[11]);
	rw = (((x * (matrix[12])) + (y * (matrix[13]))) + (z * (matrix[14]))) + (matrix[15]);
	if (rw == 1.0) {
		pVertex[0] = (((float) rx));
		pVertex[1] = (((float) ry));
		pVertex[2] = (((float) rz));
	} else {
		if (rw == 0.0) {
			rw = 0.0;
		} else {
			rw = 1.0 / rw;
		}
		pVertex[0] = (((float) (rx * rw)));
		pVertex[1] = (((float) (ry * rw)));
		pVertex[2] = (((float) (rz * rw)));
	}
}


/*	Transform the position of the given primitive vertex assuming that 
	matrix a41 = a42 = a43 = 0.0 and a44 = 1.0 */

static int transformPrimitivePositionFastby(float *pVertex, float *matrix) {
    double x;
    double rx;
    double y;
    double ry;
    double z;
    double rz;

	x = pVertex[0];
	y = pVertex[1];
	z = pVertex[2];
	rx = (((x * (matrix[0])) + (y * (matrix[1]))) + (z * (matrix[2]))) + (matrix[3]);
	ry = (((x * (matrix[4])) + (y * (matrix[5]))) + (z * (matrix[6]))) + (matrix[7]);
	rz = (((x * (matrix[8])) + (y * (matrix[9]))) + (z * (matrix[10]))) + (matrix[11]);
	pVertex[0] = (((float) rx));
	pVertex[1] = (((float) ry));
	pVertex[2] = (((float) rz));
}


/*	Transform the position of the given primitive vertex assuming that 
	matrix a14 = a24 = a34 = a41 = a42 = a43 = 0.0 and a44 = 1.0 */

static int transformPrimitivePositionFasterby(float *pVertex, float *matrix) {
    double x;
    double rx;
    double y;
    double ry;
    double z;
    double rz;

	x = pVertex[0];
	y = pVertex[1];
	z = pVertex[2];
	rx = ((x * (matrix[0])) + (y * (matrix[1]))) + (z * (matrix[2]));
	ry = ((x * (matrix[4])) + (y * (matrix[5]))) + (z * (matrix[6]));
	rz = ((x * (matrix[8])) + (y * (matrix[9]))) + (z * (matrix[10]));
	pVertex[0] = (((float) rx));
	pVertex[1] = (((float) ry));
	pVertex[2] = (((float) rz));
}


/*	Transform the normal of the given primitive vertex */

static int transformPrimitiveRasterPositionby(float *pVertex, float *matrix) {
    double rw;
    double x;
    double rx;
    double y;
    double ry;
    double z;
    double rz;

	x = pVertex[0];
	y = pVertex[1];
	z = pVertex[2];
	rx = (((x * (matrix[0])) + (y * (matrix[1]))) + (z * (matrix[2]))) + (matrix[3]);
	ry = (((x * (matrix[4])) + (y * (matrix[5]))) + (z * (matrix[6]))) + (matrix[7]);
	rz = (((x * (matrix[8])) + (y * (matrix[9]))) + (z * (matrix[10]))) + (matrix[11]);
	rw = (((x * (matrix[12])) + (y * (matrix[13]))) + (z * (matrix[14]))) + (matrix[15]);
	pVertex[8] = (((float) rx));
	pVertex[9] = (((float) ry));
	pVertex[10] = (((float) rz));
	pVertex[11] = (((float) rw));
}


/*	Transform the entire vertex array by the given matrices */
/*	TODO: Check the actual trade-offs between vtxCount and analyzing */

static int transformVBcountbyandflags(float *vtxArray, int vtxCount, float *modelViewMatrix, float *projectionMatrix, int flags) {
    int hasNormals;
    int prFlags;
    float *pVertex;
    int mvFlags;
    int i;
    int rescale;

	mvFlags = analyzeMatrix(modelViewMatrix);
	prFlags = analyzeMatrix(projectionMatrix);
	pVertex = ((float *) vtxArray);

	/* Check if we have to rescale the normals */

	hasNormals = flags & 16;
	if (hasNormals) {
		if (mvFlags & 1) {
			rescale = 0;
		} else {
			rescale = analyzeMatrix3x3Length(modelViewMatrix);
		}
	}
	if ((mvFlags & 2) && (prFlags == 0)) {
		if ((mvFlags == 4) == 0) {
			for (i = 1; i <= vtxCount; i += 1) {
				if (hasNormals) {
					transformPrimitiveNormalbyrescale(pVertex, modelViewMatrix, rescale);
				}
				transformPrimitivePositionFastby(pVertex, modelViewMatrix);
				transformPrimitiveRasterPositionby(pVertex, projectionMatrix);
				pVertex += 16;
			}
		} else {
			for (i = 1; i <= vtxCount; i += 1) {
				if (hasNormals) {
					transformPrimitiveNormalbyrescale(pVertex, modelViewMatrix, rescale);
				}
				transformPrimitivePositionFasterby(pVertex, modelViewMatrix);
				transformPrimitiveRasterPositionby(pVertex, projectionMatrix);
				pVertex += 16;
			}
		}
		return null;
	}
	if ((mvFlags & prFlags) & 1) {
		for (i = 1; i <= vtxCount; i += 1) {
			pVertex[8] = (pVertex[0]);
			pVertex[9] = (pVertex[1]);
			pVertex[10] = (pVertex[2]);
			pVertex[11] = 1.0;
			pVertex += 16;
		}
		return null;
	}
	if (mvFlags & 1) {
		for (i = 1; i <= vtxCount; i += 1) {
			transformPrimitiveRasterPositionby(pVertex, projectionMatrix);
			pVertex += 16;
		}
		return null;
	}
	if (prFlags & 1) {
		for (i = 1; i <= vtxCount; i += 1) {
			if (hasNormals) {
				transformPrimitiveNormalbyrescale(pVertex, modelViewMatrix, rescale);
			}
			if (mvFlags == (2 + 2)) {
				transformPrimitivePositionFasterby(pVertex, modelViewMatrix);
			} else {
				if (mvFlags == 2) {
					transformPrimitivePositionFastby(pVertex, modelViewMatrix);
				} else {
					transformPrimitivePositionby(pVertex, modelViewMatrix);
				}
			}
			pVertex[8] = (pVertex[0]);
			pVertex[9] = (pVertex[1]);
			pVertex[10] = (pVertex[2]);
			pVertex[11] = 1.0;
			pVertex += 16;
		}
		return null;
	}
	for (i = 1; i <= vtxCount; i += 1) {
		if (hasNormals) {
			transformPrimitiveNormalbyrescale(pVertex, modelViewMatrix, rescale);
		}
		transformPrimitivePositionby(pVertex, modelViewMatrix);
		transformPrimitiveRasterPositionby(pVertex, projectionMatrix);
		pVertex += 16;
	}
}


/*	Load the word based array of size count from the given oop */

static void* vbLoadArraysize(int oop, int count) {
	if (oop == null) {
		interpreterProxy->primitiveFail();
		return null;
	}
	if (oop == (interpreterProxy->nilObject())) {
		return null;
	}
	if (!(interpreterProxy->isWords(oop))) {
		interpreterProxy->primitiveFail();
		return null;
	}
	if (!((interpreterProxy->slotSizeOf(oop)) == count)) {
		interpreterProxy->primitiveFail();
		return null;
	}
	return interpreterProxy->firstIndexableField(oop);
}
