/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1997  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include "pm.h"

static VPoint origin =
{0.0, 0.0, 0.0};

void
placeCraft(Viewport * v, 
		   craft * c, 
		   viewer *u,
		   craft * obj, 
		   VPolygon ** poly, 
		   long *cnt)
{

	int       i, k, n;
	long      j;
	VPoint   *q, tmp, tmp1, center, nc;
	VMatrix   mtx, mtx1, m, m1;
	VPolygon **p, *p1;
	double    dist;
	int       aspect, offset;
	VObject  *op;

	j = *cnt;

/*
 *  Build a transformation matrix to convert from object to world coordinates.
 */

	if ((obj->flags & FL_FIXED_OBJECT) == 0) {

		VIdentMatrix(&m);

		VRotate(&m, YRotation, DEGtoRAD(90.0));
		VRotate(&m, YRotation, obj->w.latitude);
		VRotate(&m, ZRotation, obj->w.longitude);

		buildEulerMatrix(obj->curRoll,
						 obj->curPitch,
						 obj->curHeading,
						 &m1);

		VMatrixMultByRank(&m1, &m, &mtx, 3);

		VTranslatePoint(&mtx, obj->Sg);
	}
	else {
		VIdentMatrix(&mtx);
	}

/*
 *  If the object uses a special method to render itself, call that procedure.
 */

	if (obj->cinfo->placeProc != NULL) {
		(*obj->cinfo->placeProc) (v, obj, &mtx, poly, cnt);
		return;
	}

/*
 *  Build a matrix to transform from world to eye coordinate systems.
 */

	VMatrixMult(&mtx, &v->eyeSpace, &mtx1);

/*
 *  Reject the object if is completely outside any of the clipping planes
 */

	if (obj->flags & FL_FIXED_OBJECT)
		tmp = obj->cinfo->object->center;
	else
		VTransform(&obj->cinfo->object->center, &mtx, &tmp);

	VTransform(&tmp, &u->v->eyeSpace, &center);
	for (i = 0; i < 4; ++i) {
		dist = VPointToClipPlaneDistance(&center,
										 &(u->v->clipNormals[i]));
		if (dist > obj->cinfo->object->extent)
			return;
	}

	op = obj->cinfo->object;
	n = op->numPolys;
	p = op->polygon;
	if (op->order) {
		VTransform(&origin, &mtx1, &nc);
		VTransform_(&_VUnitVectorI, &mtx1, &op->xaxis);
		VTransform_(&_VUnitVectorJ, &mtx1, &op->yaxis);
		VTransform_(&_VUnitVectorK, &mtx1, &op->zaxis);
		aspect = VComputeObjectAspect(op, &nc);
#ifdef notdef
		printf("%s: %s\n", op->name, VGetAspectName(aspect));
#endif
		offset = aspect * n;
	}

	for (i = 0; i < n; ++i) {

/*
 *  Here is an opportunity to cull back-facing polygons without
 *  copying or transforming them. tmp1 becomes the transformed coordinates
 *  of the first vertex in the polygon.
 */

		p1 = (op->order) ? p[op->order[offset + i]] : p[i];

		VTransform(&p1->vertex[0], &mtx1, &tmp1);
		if (p1->flags & PolyNormalValid) {
			VTransform_(&p1->normal, &mtx1, &tmp);
		}
		if (p1->flags & PolyClipBackface) {
			if (VDotProd(&tmp1, &tmp) >= 0.0) {
				continue;
			}
		}

/*
 *  Beyond the culling distance?  Don't plot this polygon
 */

		if (p1->flags & PolyUseCullDistance) {
			if (sqrt(tmp1.x * tmp1.x + tmp1.y * tmp1.y + tmp1.z * tmp1.z) >
				p1->cullDistance) {
				continue;
			}
		}

		poly[j] = VCopyPolygon(p1);

/*
 *  First copy the normal vector and the transformed first point, then loop
 *  over all remaining points.
 */

		if (poly[j]->flags & PolyNormalValid) {
			poly[j]->normal = tmp;
		}

		q = poly[j]->vertex;

/*
 *  First, copy the previously transformed first vertex.
 */

		*q = tmp1;
		q++;

/*
 *  Now transform and copy the remaining vertices.
 */

		for (k = 1; k < poly[j]->numVtces; (++k, ++q)) {
			VTransform(q, &mtx1, &tmp);
			*q = tmp;
		}
		++j;
	}

	*cnt = j;
}

/*
 *  OBSOLETE CODE: we no longer need this function.  We used to transform
 *  objects to their world coordinates and then transform them to eye
 *  coordinates.  Those are now consolidated into one step.
 *
 *  Place an object in world space based on the template object description,
 *  Euler angles, and [xyz] location.  This procedure is called only once
 *  for each fixed object (in init.c).
 */

#ifdef notdef

void
placeObject(obj)
craft    *obj;
{

	register int i, j, n;
	VPoint   *q, tmp;
	VMatrix   mtx;
	VPolygon **p;
	VObject  *newObj;

	VIdentMatrix(&mtx);
	if (obj->curRoll != 0.0)
		VRotate(&mtx, XRotation, obj->curRoll);
	if (obj->curPitch != 0.0)
		VRotate(&mtx, YRotation, -obj->curPitch);
	if (obj->curHeading != 0.0)
		VRotate(&mtx, ZRotation, obj->curHeading);
	VTranslatePoint(&mtx, obj->Sg);

	if (obj->cinfo->placeProc != NULL) {
		fprintf(stderr, "This object cannot be placed this way.\n");
		exit(1);
	}

	newObj = VCopyObject(obj->cinfo->object);

	n = newObj->numPolys;
	p = newObj->polygon;
	for (i = 0; i < n; ++i) {
		for ((j = 0, q = p[i]->vertex); j < p[i]->numVtces; (++j, ++q)) {
			VTransform(q, &mtx, &tmp);
			*q = tmp;
		}
		if (p[i]->flags & PolyNormalValid) {
			VTransform(&p[i]->normal, &mtx, &tmp);
			p[i]->normal = tmp;
			p[i]->normal.x -= mtx.m[0][3];
			p[i]->normal.y -= mtx.m[1][3];
			p[i]->normal.z -= mtx.m[2][3];

		}
	}

	VTransform(&newObj->center, &mtx, &tmp);
	newObj->center = tmp;

	obj->cinfo->object = newObj;

	obj->flags |= FL_FIXED_OBJECT;
}

#endif
