/* Copyright (C) 1999-2003 by Peter Eastman

   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; either version 2 of the License, or (at your option) any later version.

   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. */

package artofillusion;

import artofillusion.math.*;
import artofillusion.material.*;
import artofillusion.texture.*;

/** A RenderingMesh represents an object to be rendered to the screen.  It is described by
    an array of RenderingTriangles, and arrays describing the positions, normals, and 
    parameter values of the vertices.  If any normal vector is null, then each triangle 
    uses its own normal vector at the corresponding vertex. */

public class RenderingMesh
{
  public Vec3 vert[], norm[], faceNorm[];
  public ParameterValue param[];
  public double paramTemp[];
  public RenderingTriangle triangle[];
  public TextureMapping mapping;
  public MaterialMapping matMapping;

  /** Construct a rendering mesh. */

  public RenderingMesh(Vec3 vert[], Vec3 norm[], RenderingTriangle triangle[], TextureMapping mapping, MaterialMapping matMapping)
  {
    int i, j;
    Vec3 v;
    double length;

    this.vert = vert;
    this.norm = norm;
    this.triangle = triangle;
    this.mapping = mapping;
    this.matMapping = matMapping;
    faceNorm = new Vec3 [triangle.length];

    // Compute the normals for each face.

    for (i = 0; i < triangle.length; i++)
      {
	faceNorm[i] = vert[triangle[i].v2].minus(vert[triangle[i].v1]).cross(vert[triangle[i].v3].minus(vert[triangle[i].v1]));
	length = faceNorm[i].length();
	if (length > 0.0)
	  faceNorm[i].scale(1.0/length);
	triangle[i].setMesh(this, i);
      }

    // If any triangles refer to null normal vectors, replace them by the triangle normals.

    for (i = 0, j = 0; i < triangle.length; i++)
      if (norm[triangle[i].n1] == null || norm[triangle[i].n2] == null || norm[triangle[i].n3] == null)
        j++;
    if (j == 0)
      return;
    Vec3 newNorm [] = new Vec3 [norm.length+j];
    for (i = 0; i < norm.length; i++)
      newNorm[i] = norm[i];
    for (i = 0, j = norm.length; i < triangle.length; i++)
      if (norm[triangle[i].n1] == null || norm[triangle[i].n2] == null || norm[triangle[i].n3] == null)
	{
	  newNorm[j] = faceNorm[i];
	  if (norm[triangle[i].n1] == null)
	    triangle[i].n1 = j;
	  if (norm[triangle[i].n2] == null)
	    triangle[i].n2 = j;
	  if (norm[triangle[i].n3] == null)
	    triangle[i].n3 = j;
	  j++;
	}
    this.norm = newNorm;
  }

  /** Set the texture parameters for the mesh. */

  public void setParameters(ParameterValue param[])
  {
    this.param = param;
    if (param == null)
    {
      paramTemp = new double [0];
      return;
    }
    paramTemp = new double [param.length];
    double val1[] = new double [param.length];
    double val2[] = new double [param.length];
    double val3[] = new double [param.length];
    for (int i = 0; i < triangle.length; i++)
    {
      RenderingTriangle tri = triangle[i];
      for (int j = param.length-1; j >= 0; j--)
      {
        val1[j] = param[j].getValue(i, tri.v1, tri.v2, tri.v3, 1.0, 0.0, 0.0);
        val2[j] = param[j].getValue(i, tri.v1, tri.v2, tri.v3, 0.0, 1.0, 0.0);
        val3[j] = param[j].getValue(i, tri.v1, tri.v2, tri.v3, 0.0, 0.0, 1.0);
      }
      mapping.setParameters(tri, val1, val2, val3, this);
    }
  }
  
  /** Apply a coordinate transformation to all of the vertices and normal vectors in this mesh. */
  
  public void transformMesh(Mat4 trans)
  {
    for (int i = 0; i < vert.length; i++)
      vert[i] = trans.times(vert[i]);
    for (int i = 0; i < norm.length; i++)
      if (norm[i] != null)
        norm[i] = trans.timesDirection(norm[i]);
    for (int i = 0; i < faceNorm.length; i++)
      faceNorm[i] = trans.timesDirection(faceNorm[i]);
  }
}