/****************************************************************************
*   PROJECT: Balloon 3D Graphics Subsystem for Squeak
*   FILE:    b3dRemap.c
*   CONTENT: Remapping functions for the B3D rasterizer
*
*   AUTHOR:  Andreas Raab (ar)
*   ADDRESS: Walt Disney Imagineering, Glendale, CA
*   EMAIL:   andreasr@wdi.disney.com
*   RCSID:   $Id$
*
*   NOTES:
*
*
*****************************************************************************/
#include "b3d.h"

/* b3dRemapFaces:
	Remap all allocated faces using the given offsets
*/
/* INLINE b3dRemapFaces(list, attrOffset, edgeOffset) */
void b3dRemapFaces(B3DFaceAllocList *list, int attrOffset, int edgeOffset)
{
	int i;

	for(i=0; i<list->size;i++) {
		B3DPrimitiveFace *face = list->data + i;
		if(face->flags & B3D_ALLOC_FLAG) {
			if(face->attributes)
				(char*)face->attributes += attrOffset;
			if(face->leftEdge)
				(char*)face->leftEdge += edgeOffset;
			if(face->rightEdge)
				(char*)face->rightEdge += edgeOffset;
		}
	}
}
/* --INLINE-- */

/* b3dRemapEdges:
	Remap all allocated edges using the given offset
*/
/* INLINE b3dRemapEdges(list, faceOffset) */
void b3dRemapEdges(B3DEdgeAllocList *list, int faceOffset)
{
	int i;
	for(i=0; i<list->size;i++) {
		B3DPrimitiveEdge *edge = list->data + i;
		if(edge->flags & B3D_ALLOC_FLAG) {
			if(edge->leftFace)
				(char*)edge->leftFace += faceOffset;
			if(edge->rightFace)
				(char*)edge->rightFace += faceOffset;
		}
	}
}
/* --INLINE-- */

/* b3dRemapFills:
	Remap the fill list using the given offset
*/
/* INLINE b3dRemapFills(fillList, offset) */
void b3dRemapFills(B3DFillList *fillList, int offset)
{
	B3DPrimitiveFace *temp;
	if(fillList->firstFace)
		(char*)fillList->firstFace += offset;
	if(fillList->lastFace)
		(char*)fillList->lastFace  += offset;
	temp = fillList->firstFace;
	while(temp) {
		if(temp->nextFace)
			(char*)temp->nextFace += offset;
		if(temp->prevFace)
			(char*)temp->prevFace += offset;
		temp = temp->nextFace;
	}
}
/* --INLINE-- */

/* b3dRemapEdgeList:
	Remap all edge pointers using the given offset
*/
/* INLINE b3dRemapEdgeList(list, edgeOffset) */
void b3dRemapEdgeList(B3DPrimitiveEdgeList *list, int edgeOffset)
{
	int i;
	for(i=0; i<list->size;i++) {
		(char*) list->data[i] += edgeOffset;
	}
}
/* --INLINE-- */

/* b3dRemapAET:
	Remap all edge pointers using the given offset
*/
/* INLINE b3dRemapAET(list, edgeOffset, aetOffset, firstEdge, lastEdge) */
void b3dRemapAET(B3DActiveEdgeTable *list, int edgeOffset, int aetOffset, void *firstEdge, void *lastEdge)
{
	int i;
	if(edgeOffset)
		for(i=0; i<list->size;i++)
			(char*) list->data[i] += edgeOffset;

	if((void*)list->leftEdge >= firstEdge && (void*)list->leftEdge < lastEdge)
		(char*) list->leftEdge += edgeOffset;
	else if(list->leftEdge)
		(char*) list->leftEdge += aetOffset;

	if((void*)list->rightEdge >= firstEdge && (void*)list->rightEdge < lastEdge)
		(char*) list->rightEdge += edgeOffset;
	else if(list->rightEdge)
		(char*) list->rightEdge += aetOffset;

	if(aetOffset) {
		(char*) list->nextIntersection += aetOffset;
		(char*) list->lastIntersection += aetOffset;
	}
}
/* --INLINE-- */

/* b3dRemapEdgeVertices:
	Remap all vertices in the specified range using the given offset
*/
/* INLINE b3dRemapEdgeVertices(list, vtxOffset, firstVtx, lastVtx) */
void b3dRemapEdgeVertices(B3DEdgeAllocList *list, int vtxOffset, void *firstVtx, void *lastVtx)
{
	int i;
	for(i=0; i<list->size; i++) {
		B3DPrimitiveEdge *edge = list->data + i;
		if((edge->flags & B3D_ALLOC_FLAG) && ((void*)edge->v0 >= (void*)firstVtx) && ((void*)edge->v0 < (void*)lastVtx)) {
			(char*) edge->v0 += vtxOffset;
			(char*) edge->v1 += vtxOffset;
		}
	}
}
/* --INLINE-- */

/* b3dRemapFaceVertices:
	Remap all vertices in the specified range using the given offset
*/
/* INLINE b3dRemapFaceVertices(list, vtxOffset, firstVtx, lastVtx) */
void b3dRemapFaceVertices(B3DFaceAllocList *list, int vtxOffset, void *firstVtx, void *lastVtx)
{
	int i;
	for(i=0; i<list->size; i++) {
		B3DPrimitiveFace *face = list->data + i;
		if((face->flags & B3D_ALLOC_FLAG) && ((void*)face->v0 >= (void*)firstVtx) && ((void*)face->v0 < (void*)lastVtx)) {
			(char*) face->v0 += vtxOffset;
			(char*) face->v1 += vtxOffset;
			(char*) face->v2 += vtxOffset;
		}
	}
}
/* --INLINE-- */

/* b3dRemapFaceFree:
	Remap all free faces using the given offset
*/
/* INLINE b3dRemapFaceFree(list, faceOffset) */
void b3dRemapFaceFree(B3DFaceAllocList *list, int faceOffset)
{
	B3DPrimitiveFace *freeObj;
	if(list->firstFree) {
		(char*)list->firstFree += faceOffset;
		freeObj = list->firstFree;
		while(freeObj->nextFree) {
			(char*) freeObj->nextFree += faceOffset;
			freeObj = freeObj->nextFree;
		}
	}
}
/* --INLINE-- */

/* b3dRemapEdgeFree:
	Remap all free edges using the given offset
*/
/* INLINE b3dRemapEdgeFree(list, edgeOffset) */
void b3dRemapEdgeFree(B3DEdgeAllocList *list, int edgeOffset)
{
	B3DPrimitiveEdge *freeObj;
	if(list->firstFree) {
		(char*)list->firstFree += edgeOffset;
		freeObj = list->firstFree;
		while(freeObj->nextFree) {
			(char*) freeObj->nextFree += edgeOffset;
			freeObj = freeObj->nextFree;
		}
	}
}
/* --INLINE-- */

/* b3dRemapAttrFree:
	Remap all free attributes using the given offset
*/
/* INLINE b3dRemapAttrFree(list, attrOffset) */
void b3dRemapAttributes(B3DAttrAllocList *list, int attrOffset)
{
	int i;
	for(i=0; i < list->size; i++) {
		B3DPrimitiveAttribute *attr = list->data + i;
		if(attr->next)
			(char*) attr->next += attrOffset;
	}
}
/* --INLINE-- */

/* b3dValidateAndRemapState:
	Validate the rasterizer state and remap the objects if necessary.
*/
int b3dValidateAndRemapState(B3DRasterizerState *state)
{
	int faceOffset, edgeOffset, attrOffset, aetOffset, objOffset, i;
	B3DPrimitiveObject *obj;

	if(!state) return B3D_GENERIC_ERROR;
	
	/* Check the magic numbers */
	if(state->faceAlloc->magic  != B3D_FACE_ALLOC_MAGIC) return B3D_MAGIC_ERROR;
	if(state->edgeAlloc->magic  != B3D_EDGE_ALLOC_MAGIC) return B3D_MAGIC_ERROR;
	if(state->attrAlloc->magic  != B3D_ATTR_ALLOC_MAGIC) return B3D_MAGIC_ERROR;
	if(state->aet->magic        != B3D_AET_MAGIC) return B3D_MAGIC_ERROR;
	if(state->addedEdges->magic != B3D_EDGE_LIST_MAGIC) return B3D_MAGIC_ERROR;
	if(state->fillList->magic   != B3D_FILL_LIST_MAGIC) return B3D_MAGIC_ERROR;

	/* Check if we need to relocate objects */
	faceOffset = (int)state->faceAlloc - (int)state->faceAlloc->This;
	edgeOffset = (int)state->edgeAlloc - (int)state->edgeAlloc->This;
	attrOffset = (int)state->attrAlloc - (int)state->attrAlloc->This;
	aetOffset = (int)state->aet - (int)state->aet->This;
	
	/* remap faces */
	if(attrOffset || edgeOffset)
		b3dRemapFaces(state->faceAlloc, attrOffset, edgeOffset);
	
	/* remap fills and edges */
	if(faceOffset) {
		b3dRemapFills(state->fillList, faceOffset);
		b3dRemapEdges(state->edgeAlloc, faceOffset);
		b3dRemapFaceFree(state->faceAlloc, faceOffset);
	}
	
	/* Remap AET */
	if(edgeOffset || aetOffset) {
		void *firstEdge = state->edgeAlloc->data;
		void *lastEdge = state->edgeAlloc->data + state->edgeAlloc->size;
		b3dRemapAET(state->aet, edgeOffset, aetOffset, firstEdge, lastEdge);
	}

	/* Remap addedEdges and edge free list*/
	if(edgeOffset) {
		b3dRemapEdgeList(state->addedEdges, edgeOffset);
		b3dRemapEdgeFree(state->edgeAlloc, edgeOffset);
	}

	if(attrOffset)
		b3dRemapAttributes(state->attrAlloc, attrOffset);
	
	state->faceAlloc->This = (void*) state->faceAlloc;
	state->edgeAlloc->This = (void*) state->edgeAlloc;
	state->attrAlloc->This = (void*) state->attrAlloc;
	state->aet->This = (void*) state->aet;

	/* Remap any vertex pointers */
	for(i=0; i<state->nObjects; i++) {
		obj = state->objects[i];
		if(obj->magic != B3D_PRIMITIVE_OBJECT_MAGIC) return B3D_MAGIC_ERROR;
		objOffset = (int)obj - (int)obj->This;
		if(objOffset) {
			if((obj->flags & B3D_OBJECT_ACTIVE)) {
				B3DPrimitiveVertex *firstVtx = obj->vertices;
				B3DPrimitiveVertex *lastVtx = obj->vertices + obj->nVertices;
				b3dRemapFaceVertices(state->faceAlloc, objOffset, firstVtx, lastVtx);
				b3dRemapEdgeVertices(state->edgeAlloc, objOffset, firstVtx, lastVtx);
			}
			obj->vertices = (B3DPrimitiveVertex*) (obj + 1);
			obj->faces = (B3DInputFace*) (obj->vertices + obj->nVertices);
		}
		obj->This = (void*) obj;
	}

	return B3D_NO_ERROR;
}
