/*-
 * Copyright (c) 1999,2000 Qube Software, Ltd.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 * 
 * 	$Id: primgen.cpp,v 1.4 2000/04/20 15:48:17 n_hibma Exp $
 */

#include <assert.h>

#include "maths.h"
#include "texmap.h"
#include "primgen.h"

void
PrimGenerator::buildDisc(float radius, float height,
			 int ringCount, unsigned int sideCount,
			 TextureMapper& mapper, Vec3f normal,
			 ftriangle addTriangle,
                         fwedge addWedge)
{
    int				i;
    unsigned int		j;

    Vec3f			v;
    Wedge			w;
    
    if (sideCount < 3)
        return;

    if (ringCount < -1)
        return;
    
    // If ringCount = -1 we still need to build the outter ring.
    
    int rings = ringCount + 1;
    if (rings == 0) rings = 1;

    unsigned int* wedges = new unsigned int[(sideCount + 1) * (rings + 1)];

    float	rad = (1.0f / (float)rings) * radius;
    float	phi = (2 * PI) / (float)sideCount;
    float	norm = Maths::dot(normal, Vec3f(0, 1, 0));
    
    if (norm > 0) {
	phi *= -1;
    } else {
	assert(norm);
    }

    v.y = height;
    w.normal = normal;

    // First put in the pole.

    if (ringCount > -1) {
	v.x = v.z = 0;
	w.texCoord = mapper.map(v);

	w.vertex = v;
	wedges[0] = addWedge(w);
    }
    
    for (i = 0; i < rings; ++i) {
	for (j = 0; j < sideCount + 1; ++j) {
	    v.x = cosf(j * phi) * (i + 1) * rad;
	    v.z = sinf(j * phi) * (i + 1) * rad;

	    w.texCoord = mapper.map(v);

	    w.vertex = v;
	    wedges[1 + j + i * (sideCount + 1)] = addWedge(w);
	}
    }

    // Build the triangles around the pole.
    if (ringCount == -1) {
    } else {
	for (j = 0; j < sideCount; ++j) {
	    addTriangle(wedges[0], wedges[j + 1], wedges[j + 2]);
	}

	for (i = 0; i < ringCount; ++i) {
	    for (j = 0; j < sideCount; ++j) {
		unsigned short k = 1 + j + i * (sideCount + 1);
		unsigned short l = k + sideCount + 1;

		addTriangle(wedges[k], wedges[l], wedges[k + 1]);
		addTriangle(wedges[l], wedges[l + 1], wedges[k + 1]);
	    }
	}
    }

    delete [] wedges;
}

void
PrimGenerator::makeCylinder(unsigned int sideCount, unsigned int sliceCount,
			    int ringCount, TextureMapper& top,
			    TextureMapper& bottom, TextureMapper& side,
                            ftriangle addTriangle,
                            fwedge addWedge)
{
    unsigned int	i;
    unsigned int	j;
    unsigned int	k = 0;
    
    if (sideCount < 3) return;
    if (sliceCount < 1) return;
    if (ringCount < 0) return;
    
    unsigned int rings = ringCount + 1;
    
    unsigned int wedgeCount =
	(sideCount * rings + 1) * 2 +	// The two faces
	((sideCount + 1) * (sliceCount + 1));	// The side

    unsigned int* wedges = new unsigned int[wedgeCount];

    // Fill in the rest of the texture mappers
    if (!top.done()) {
	top.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }

    if (!side.done()) {
	side.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }

    if (!bottom.done()) {
	bottom.directions(Vec3f(0, -1, 0), Vec3f(1, 0, 0));
    }

    Vec2f topTexCo;
    Vec2f botTexCo;
    
    if (ringCount != -1) {
	/* vertices 0 and 1 are
	 * the north and south poles
	 */
	Wedge w;
	Vec3f p(0, 1, 0);
	topTexCo = top.map(p);

	w.vertex = p;
	w.normal = Vec3f(0.0f, 1.0f, 0.0f);
	w.texCoord = Vec2f(topTexCo.x, topTexCo.y);
        wedges[0] = addWedge(w);
	
	p = Vec3f(0,-1, 0);
	botTexCo = bottom.map(p);

	w.vertex = p;
	w.normal = Vec3f(0.0f, -1.0f, 0.0f);
	w.texCoord = Vec2f(botTexCo.x, botTexCo.y);
        wedges[1] = addWedge(w);
    }


    float       dj = 1.0f / (float)rings;
    float	di = PI / (sideCount / 2.0f);
    
    /* Build the geometry
     * First the rings at the top and bottom.
     */
    for (j = 0; j < rings; j++) {
        for (i = 0; i < sideCount; i++) {
	    k = (1 + i + j * sideCount) * 2;
	    
            Vec3f p = Vec3f(sinf(i * di) * (dj * (j + 1)),
			    1.0f,
			    cosf(i * di) * (dj * (j + 1)));

	    topTexCo = top.map(p);

            Wedge w;

	    w.vertex = p;
	    w.normal = Vec3f(0.0f, 1.0f, 0.0f);
	    w.texCoord = Vec2f(topTexCo.x, topTexCo.y);
            wedges[k] = addWedge(w);
	    
            p.y = -1.0f;
	    botTexCo = bottom.map(p);
            w.vertex = p;
            w.normal = Vec3f(0.0f, -1.0f, 0.0f);
	    w.texCoord = Vec2f(botTexCo.x, botTexCo.y);
            wedges[k + 1] = addWedge(w);
        }
    }

    unsigned int topVertCount = k + 2;
    
    /* now the main body
     */
    dj = 2.0f / (float)sliceCount;

    CylinderMapHelper sideMapper(side);
    
    for (j = 0; j < sliceCount + 1; j++) {
	sideMapper.init();
	
        for (i = 0; i < sideCount + 1; i++) {
	    k = topVertCount + i + j * (sideCount + 1);
            Wedge w;
	    Vec3f p = Vec3f(sinf(i * di), 1.0f - j * dj, cosf(i * di));
	    Vec2f sideTexCo = sideMapper.map(p);

	    w.vertex = p;
	    w.normal = Vec3f(p.x, 0, p.z);
	    w.texCoord = Vec2f(sideTexCo.x, sideTexCo.y);
            wedges[k] = addWedge(w);
        }
    }
    

    /* generate the traingle indices
     * strip around north and south poles
     */
    
    for (i = 0; i < sideCount; ++i) {
	unsigned int end1 = 2 * i + 4;
	unsigned int end2 = 2 * i + 5;
	
	if (i == sideCount - 1) {
	    end1 = 2;
	    end2 = 3;
	}

	addTriangle(wedges[0], wedges[2 * i + 2], wedges[end1]);
	addTriangle(wedges[1], wedges[end2], wedges[2 * i + 3]);
    }
    
    // Now the quads on the top and bottom.
    
    for (int l = 0; l < ringCount; ++l) {
	for (i = 0; i < sideCount; ++i) {
	    k = (1 + i + l * sideCount) * 2;
	    
	    unsigned int end1 = k + 2;
	    unsigned int end2 = k + 3;
	    
	    if (i == sideCount - 1) {
		end1 = (1 + l * sideCount) * 2;
		end2 = (1 + l * sideCount) * 2 + 1;
	    }

	    addTriangle(wedges[k], wedges[k + 2 * sideCount], wedges[end1]);
	    addTriangle(wedges[k + 2 * sideCount],
			wedges[end1 + 2 * sideCount],
			wedges[end1]);

	    addTriangle(wedges[k + 1], wedges[end2],
			wedges[k + 1 + 2 * sideCount]);
	    addTriangle(wedges[end2], wedges[end2 + 2 * sideCount],
			wedges[k + 1 + 2 * sideCount]);
	}
    }
 
    /* All the middle quads
     */

    for (j = 0; j < sliceCount; j++) {
        for (i = 0; i < sideCount; i++) {
	    k = topVertCount + i + j * (sideCount + 1);

	    addTriangle(wedges[k], wedges[k + sideCount + 1], wedges[k + 1]);
	    addTriangle(wedges[k + sideCount + 1],
			wedges[k + 2 + sideCount], wedges[k + 1]);
        }
    }    

    delete [] wedges;
}

void
PrimGenerator::makeSphere(unsigned int sideCount,
			  unsigned int sliceCount,
			  TextureMapper& map,
                          ftriangle addTriangle,
                          fwedge addWedge)
{
    unsigned int	i;
    unsigned int	j;
    unsigned int	k;
    
    if (sideCount < 3) return;

    if (sliceCount < 2) return;
    
    unsigned int poleWedgeCount = 2 * (sideCount + 1);
    unsigned int wedgeCount = (sliceCount - 1) *
	(sideCount + 1) + poleWedgeCount;

    unsigned int* wedges = new unsigned int[wedgeCount];

    if (!map.done()) {
	map.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }
    
    float dj = PI / (float)sliceCount;
    float di = PI / (sideCount / 2.0f);
    
    // vertices 0 and 1 are the north and south poles

    Vec2f texCo;
    
    Vec3f topVert(0, 1, 0);
    Vec3f botVert(0, -1, 0);

    /* Build the geometry
     */
    CylinderMapHelper mapper(map);
    
    for (j = 0; j < sliceCount - 1; j++) {
	mapper.init();
        for (i = 0; i < sideCount + 1; i++) {
	    k = poleWedgeCount + i + j * (sideCount + 1);
            Wedge w;
	    Vec3f p = Vec3f(sinf(i * di) * (float) sin((j + 1) * dj),
			    cosf((j + 1) * dj),
			    cosf(i * di) * (float) sin((j + 1) * dj));

	    w.vertex = p;
	    w.normal = p;
	    texCo = mapper.map(p);
            w.texCoord = texCo;
            wedges[k] = addWedge(w);

	    if (j == 0) {
		// Add the extra wedges to the poles
		w.normal = Vec3f(0, 1, 0);
		w.texCoord = Vec2f(texCo.x, map.map(w.normal).y);
                w.vertex = topVert;
                wedges[2 * i] = addWedge(w);

		w.normal = Vec3f(0, -1, 0);
		w.texCoord = Vec2f(texCo.x, map.map(w.normal).y);
                w.vertex = botVert;
                wedges[2 * i + 1] = addWedge(w);
	    }
	}
    }

    /* generate the traingle indices
     * strip around north and south poles
     */
    for (i = 0; i < sideCount; i++) {
	k = i + poleWedgeCount;
	int j = k + (sideCount + 1) * (sliceCount - 2);
	
	addTriangle(wedges[2 * i], wedges[k], wedges[k + 1]);
	addTriangle(wedges[2 * i + 1], wedges[j + 1], wedges[j]);
    }
    
    /* All the middle quads
     */
    for (j = 0; j < sliceCount - 2; j++) {

        k = poleWedgeCount + j * (sideCount + 1);

        for (i = 0; i < sideCount; i++) {
	    addTriangle(wedges[k + i], wedges[k + i + 1 + sideCount],
			wedges[k + i + 1]);
	    addTriangle(wedges[k + i + 1 + sideCount],
			wedges[k + i + 2 + sideCount],
			wedges[k + i + 1]);
        }
    }

    delete [] wedges;
}    

void
PrimGenerator::makeFrustum(float smallRad,
			   unsigned int sideCount,
			   unsigned int sliceCount,
			   int ringCount,
			   TextureMapper& top,
			   TextureMapper& bottom,
			   TextureMapper& side,
                           ftriangle addTriangle,
                           fwedge addWedge)
{
    CylinderMapHelper		mapper(side);

    unsigned int		i;
    unsigned int		j;
    
    if (smallRad < 0 || smallRad > 1) return;
    if (sideCount < 3) return;
    if (sliceCount < 1)	return;
    if (ringCount < -1) return;

    if (!top.done()) {
	top.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }
    
    if (!side.done()) {
	side.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }

    if (!bottom.done()) {
	bottom.directions(Vec3f(0, -1, 0), Vec3f(1, 0, 0));
    }
    
    if (smallRad > 0) {
	// need to build a top disc

	buildDisc(smallRad, 1.0f, ringCount, sideCount, top,
		  Vec3f(0, 1, 0), addTriangle, addWedge);
    }

    buildDisc(1, 0, ringCount, sideCount, bottom, Vec3f(0, -1, 0),
	      addTriangle, addWedge);

    // Now build the side
    unsigned int* wedges = new unsigned int[(sliceCount + 1) * (sideCount + 1)];
    
    // Angle of the side to the base
    float theta = atan2f(1, 1 - smallRad);
    float sinTheta = sinf(theta);
    float cosTheta = cosf(theta);

    // Angle for going around the circle
    float phi = (2 * PI) / (float)sideCount;
    float height = 1 / (float)sliceCount;

    // Normal has constant y component.
    Wedge w;

    w.normal.y = cosTheta;
    
    for (i = 0; i < sliceCount + 1; ++i) {
	Vec3f v;

	v.y = i * height;
	
	float rad = 1 - v.y * (1 - smallRad);

	mapper.init();

	for (j = 0; j < sideCount + 1; ++j) {
	    v.x = cosf(j * phi) * rad;
	    v.z = sinf(j * phi) * rad;

	    w.texCoord = mapper.map(v);
	    w.normal.x = sinTheta * cosf(j * phi);
	    w.normal.z = sinTheta * sinf(j * phi);

	    w.vertex = v;
	    wedges[j + i * (sideCount + 1)] = addWedge(w);
	}
    }

    // Now build the faces

    for (i = 0; i < sliceCount; ++i) {
	for (j = 0; j < sideCount; ++j) {
	    unsigned short k = j + i * (sideCount + 1);
	    unsigned short l = k + sideCount + 1;

	    addTriangle(wedges[k], wedges[l], wedges[k + 1]);
	    addTriangle(wedges[l], wedges[l + 1], wedges[k + 1]);
	}
    }

    delete [] wedges;
}

void
PrimGenerator::makeTriangle(TextureMapper& map,
                            ftriangle addTriangle,
                            fwedge addWedge)
{
    unsigned int	i1, i2, i3;
    /*
    if (!map.done()) {
	map.directions(Vec3f(0, 1, 0), Vec3f(1, 0, 0));
    }
    */
    Wedge w1,w2,w3;

    //mapper.init();

    w1.vertex = Vec3f(0, 1, 0);
    w1.normal = Vec3f(0, 0, -1);
    //w1.texCoord = mapper.map(w1.vertex);
    i1 = addWedge(w1);

    w2.vertex = Vec3f(-0.5f, 0, 0);
    w2.normal = Vec3f(0, 0, -1);
    // w2.texCoord = mapper.map(w2.vertex);
    i2 = addWedge(w2);

    w3.vertex = Vec3f(0.5f, 0, 0.1f);
    w3.normal = Vec3f(0, 0, -1);
    //w3.texCoord = mapper.map(w3.vertex);
    i3 = addWedge(w3);

    addTriangle(i3, i2, i1);
#if 0

    w1.vertex = Vec3f(0, 1, 0);
    w1.normal = Vec3f(0, 0, -1);
    //w1.texCoord = mapper.map(w1.vertex);
    i1 = addWedge(w1);

    w2.vertex = Vec3f(-0.5f, 0, 0);
    w2.normal = Vec3f(0, 0, -1);
    // w2.texCoord = mapper.map(w2.vertex);
    i2 = addWedge(w2);

    w3.vertex = Vec3f(0.5f, 0, 0);
    w3.normal = Vec3f(0, 0, -1);
    //w3.texCoord = mapper.map(w3.vertex);
    i3 = addWedge(w3);

    addTriangle(i1, i2, i3);

#endif
}    
