Скачиваний:
28
Добавлен:
02.05.2014
Размер:
18.88 Кб
Скачать
// Unitdisk.cpp: implementation of the Unitdisk class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Lighting.h"

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

#include "Unitdisk.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

const float fudge = .000001;
const float M_2PI = 2.0 * M_PI;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Unitdisk::Unitdisk()
{
  rdivisions = 3;
  tdivisions = 10;
  points = normals = NULL;
  colors = NULL;
  points_size = normals_size = colors_size = 0;
  angle = M_PI;
  zaxis[0] = 0;
  zaxis[1] = 0;
  zaxis[2] = 1;

  still_in_xy = 1;

  sintable = costable = NULL;
}


Unitdisk::~Unitdisk()
{
}

void Unitdisk::draw()
{
  if(points == NULL) return;
  glNormal3f(0, 0, 1);
  if(colors == NULL)
    draw_nocolors();
  else
    if(normals == NULL)
      draw_colors_nonormals();
  else
    draw_colors_normals();
}

void Unitdisk::draw_nocolors()
{
  int r, t, p1, p2;
  int has_n;

  has_n = (normals != NULL);

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      if (has_n) glNormal3fv(normals[p1].pt);
      glVertex3fv(points[p1].pt);
      if (has_n) glNormal3fv(normals[p2].pt);
      glVertex3fv(points[p2].pt);
    } 
    glEnd(); 
  }
  
  glBegin(GL_QUAD_STRIP); 
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    if (has_n) glNormal3fv(normals[p1].pt);
    glVertex3fv(points[p1].pt);
    if (has_n) glNormal3fv(normals[p2].pt);
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_colors_nonormals()
{  
  int r, t, p1, p2;

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      glColor4fv(colors[p1].c);
      glVertex3fv(points[p1].pt);
      glColor4fv(colors[p2].c);
      glVertex3fv(points[p2].pt);
    } 
    glEnd(); 
  }
  
  glBegin(GL_QUAD_STRIP); 
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    glColor4fv(colors[p1].c);
    glVertex3fv(points[p1].pt);
    glColor4fv(colors[p2].c);
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_colors_normals()
{
  int r, t, p1, p2;

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      glColor4fv(colors[p1].c);
      glNormal3fv(normals[p1].pt);
      glVertex3fv(points[p1].pt);
      glColor4fv(colors[p2].c);
      glNormal3fv(normals[p2].pt);
      glVertex3fv(points[p2].pt);
    } 
    glEnd(); 
  }
  
  glBegin(GL_QUAD_STRIP); 
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    glColor4fv(colors[p1].c);
    glNormal3fv(normals[p1].pt);
    glVertex3fv(points[p1].pt);
    glColor4fv(colors[p2].c);
    glNormal3fv(normals[p2].pt);
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_by_perimeter(int pass_colors, int pass_norms, 
				 int pass_tex)
{
  int i, index, r = rdivisions + 1;

  if (points == NULL) return;
  if (pass_colors && colors == NULL) {
    pass_colors = 0;
  }
  if (pass_norms && normals == NULL) {
    pass_norms = 0;
  }
  glBegin(GL_POLYGON);
  for (i = 0, index = rdivisions; i < tdivisions; i++, index += r) {
    if (pass_colors) glColor4fv(colors[index].c);
    if (pass_norms) glNormal3fv(normals[index].pt);
    glVertex3fv(points[index].pt);
  }
  glEnd();
  
}

void Unitdisk::set_angle(float new_angle)
{
  angle = new_angle;
}

GLfloat Unitdisk::get_angle()
{
  return angle;
}

GLfloat Unitdisk::get_radius()
{
  return (GLfloat)cos((double)((M_PI - angle) / 2.0));
}

void Unitdisk::set_divisions(int new_rdivisions, int new_tdivisions)
{
  if (tdivisions != new_tdivisions) {
    delete sintable;
    delete costable;
    sintable = costable = NULL;
  }
  if (tdivisions != new_tdivisions || rdivisions != new_rdivisions) {
    rdivisions = new_rdivisions;
    tdivisions = new_tdivisions;
    free_points();
    free_normals();
    free_colors();
  }
}
  
int Unitdisk::get_rdivisions()
{
  return rdivisions;
}

int Unitdisk::get_tdivisions()
{
  return tdivisions;
}

void Unitdisk::alloc_points()
{
  int npoints = get_npoints();
  if (npoints > points_size) {
    delete points;
    points = new Point[npoints];
    points_size = npoints;
  }
}

void Unitdisk::alloc_normals()
{
  int npoints = get_npoints();
  if (npoints > normals_size) {
    delete normals;
    normals = new Point[npoints];
    normals_size = npoints;
  }
}

void Unitdisk::alloc_points_normals()
{
  alloc_points();
  alloc_normals();
}

void Unitdisk::free_points() 
{
  delete points;
  points = NULL;
  points_size = 0;
}

void Unitdisk::free_normals()
{
  delete normals;
  normals = NULL;
  normals_size = 0;
}

void Unitdisk::free_points_normals()
{
  free_points();
  free_normals();
}

void Unitdisk::fill_points()
{
  alloc_points();
  fill_either(points);
}

void Unitdisk::fill_normals()
{
  alloc_normals();
  fill_either(normals);
}

void Unitdisk::fill_points_normals()
{
  alloc_points();
  alloc_normals();
  fill_either(points);
  fill_either(normals);
}

void Unitdisk::copy_points(Unitdisk src) 
{
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_points();
  copy_either(points, src.points);
}

void Unitdisk::copy_normals(Unitdisk src) 
{
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_normals();
  copy_either(normals, src.normals);
}

void Unitdisk::copy_either(Point *dpt, Point *spt) {
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++, dpt++, spt++) {
    dpt->pt[0] = spt->pt[0];
    dpt->pt[1] = spt->pt[1];
    dpt->pt[2] = spt->pt[2];
  }
}  

void Unitdisk::copy_normals_from_points(Unitdisk src) {
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_normals();
  copy_either(normals, src.points);
}

void Unitdisk::copy_normals_from_points() {
  copy_normals_from_points(*this);
}

void Unitdisk::fill_either(Point *what) {
  int t, r;
  int i;

  fill_either_strip1(what);
  if (sintable == NULL) fill_trig_tables();
  i = rdivisions + 1;
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      what[i].pt[0] = costable[t] * what[r].pt[0];
      what[i].pt[1] = sintable[t] * what[r].pt[0];
      what[i].pt[2] = what[r].pt[2];
      i++;
    }
  }
}

void Unitdisk::fill_points_strip1()
{
  alloc_points();
  fill_either_strip1(points);
}

void Unitdisk::fill_either_strip1(Point *what) {
  float radius, rinc;
  int r;

  rinc = get_radius() / (float)rdivisions;
  radius = 0.0;
  for (r = 0; r <= rdivisions; r++, radius += rinc) {
    what[r].pt[0] = radius;
    what[r].pt[1] = 0;

    what[r].pt[2] = (GLfloat)(1.0 - what[r].pt[0]*what[r].pt[0]);
    if (what[r].pt[2] > 0.0) what[r].pt[2] = (GLfloat)sqrt((double)what[r].pt[2]);
    else what[r].pt[2] = 0.0;
  }
}

void Unitdisk::translate(Point trans)
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] += trans.pt[0];
    points[i].pt[1] += trans.pt[1];
    points[i].pt[2] += trans.pt[2];
  }
}

void Unitdisk::scale(float s) 
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] *= s;
    points[i].pt[1] *= s;
    points[i].pt[2] *= s;
  }
}

void Unitdisk::scale_translate(float s, Point trans) 
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] = points[i].pt[0]*s + trans.pt[0];
    points[i].pt[1] = points[i].pt[1]*s + trans.pt[1];
    points[i].pt[2] = points[i].pt[2]*s + trans.pt[2];
  }
}

int Unitdisk::get_npoints() 
{
  return (rdivisions + 1) * tdivisions;
}

void Unitdisk::project()
{
  int i, npoints;

  if (normals == NULL) {
    fill_normals();
  }
  if (points == NULL) fill_points();
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    if (normals[i].pt[2] < 0.0) normals[i].pt[2] = -normals[i].pt[2];
    points[i] = points[i].project_direction(normals[i]);
  }
}

void Unitdisk::project(Point projpt)
{
  int i, npoints = get_npoints();
  float x, y, z;
  Point *pt;

  if (points == NULL) fill_points();
  x = projpt.pt[0];
  y = projpt.pt[1];
  z = projpt.pt[2];
  pt = points;
  for (i = 0; i < npoints; i++, pt++) pt->project_self(x, y, z);
}

void Unitdisk::project_borrow_points(Unitdisk src) 
{
  int i, npoints = get_npoints();
  Point *pt, *spt, *sn;
  spt = src.points;
  sn = normals;
  alloc_points();
  pt = points;
  for (i = 0; i < npoints; i++, pt++, spt++, sn++) {
    if (normals[i].pt[2] < 0.0) normals[i].pt[2] = -normals[i].pt[2];
    pt->compute_projected(spt->pt[0], spt->pt[1], spt->pt[2], 
			  sn->pt[0], sn->pt[1], sn->pt[2]);
  }
}

void Unitdisk::refract_normals(Point light, GLfloat I)
{
  Point dlight;
  float cos1, sin1, cos2, sin2;
  int use_normals;
  int r, t, i;

  if (points == NULL) {
    fill_normals();
  }

  use_normals = (normals != NULL);
  alloc_normals();

  // Для диагонали сделать theta = 0
  for (r = 0; r <= rdivisions; r++) {
  
    // Найти исходную нормаль - использовать единицы точки,
    //если это не нормаль
    if (!use_normals) normals[r] = points[r].unit();

    // Рассчитываем направление источника света
    dlight = (light - points[r]).unit();

    // Рассчитываем синус и косинус исходного угла
    cos1 = dlight.dot(normals[r]);
    sin1 = (float)(1.0 - cos1*cos1);
    if (sin1 <= 0.0) continue;
    sin1 = (float)sqrt((double)sin1);

    // Рассчитываем синус и косинус нового угла
    sin2 = sin1 / I;
    cos2 = (float)sqrt((double)(1.0 - sin2*sin2));

    // Вращаем нормаль
    normals[r] = normals[r].rotate_abouty(cos2, -sin2);
  }

  // Копируем остальные строки из текущей
  i = rdivisions + 1;
  if (sintable == NULL) fill_trig_tables();
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      normals[i].pt[0] = costable[t] * normals[r].pt[0];
      normals[i].pt[1] = sintable[t] * normals[r].pt[0];
      normals[i].pt[2] = normals[r].pt[2];
      i++;
    }
  }
}

void Unitdisk::face_direction(Point d)
{
  face_direction(d, *this);
}

void Unitdisk::face_direction(Point d, Unitdisk src) 
{
  Point *spt, *dpt, *sn, *dn;
  float sin1, cos1;
  float x;
  int npoints, i;

  if (d.pt[1]) {
    return;
  }

  cos1 = d.pt[2];
  sin1 = d.pt[0];
  
  if (sin1 * sin1 > fudge) {
    spt = src.points;
    dpt = points;
    sn = src.normals;
    dn = normals;
    npoints = get_npoints();
    for (i = 0; i < npoints; i++) {
      if (points != NULL) {
	x = spt->pt[0];
	dpt->pt[0] = x*cos1 + spt->pt[2]*sin1;
	dpt->pt[1] = spt->pt[1];
	dpt->pt[2] = -x*sin1 + spt->pt[2]*cos1;
	spt++; dpt++;
      }
      if (normals != NULL) {
	x = sn->pt[0];
	dn->pt[0] = x*cos1 + sn->pt[2]*sin1;
	dn->pt[1] = sn->pt[1];
	dn->pt[2] = -x*sin1 + sn->pt[2]*cos1;
	sn++; dn++;
      }
    }
  } else if (points != NULL && points != src.points) {
    copy_points(src);
    if (normals != NULL) copy_normals(src);
  }
}  

void Unitdisk::alloc_colors()
{
  int ncolors = get_npoints();
  if (ncolors > colors_size) {
    delete colors;
    colors = new Color[ncolors];
    colors_size = ncolors;
  }
}

void Unitdisk::map_normals_to_colors() 
{
  int t, r;
  int i;

  if (normals == NULL) fill_normals();
  alloc_colors();
  i = 0;
  for (t = 1; t <= tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      colors[i] *= normals[r].pt[2];
      colors[i].c[3] = 1;
      i++;
    }
  }
}

void Unitdisk::map_z_to_colors()
{
  int i, npoints = get_npoints();

  if (points == NULL) {
    fill_points();
  }

  alloc_colors();
  for (i = 0; i < npoints; i++) {
    colors[i] = points[i].pt[2];
    colors[i].c[3] = 1;
  }
}

void Unitdisk::scale_alpha_by_z()
{
  int i, npoints = get_npoints();
  
  if (colors == NULL) alloc_colors();
  if (points == NULL) {
    alloc_points();
    fill_points();
  }
  for (i = 0; i < npoints; i++) colors[i].c[3] *= points[i].pt[2];
}

void Unitdisk::scale_colors_by_z() 
{
  int i, npoints = get_npoints();

  if (colors == NULL) alloc_colors();
  if (points == NULL) {
    alloc_points();
    fill_points();
  }
  for (i = 0; i < npoints; i++) colors[i] *= points[i].pt[2];
}

void Unitdisk::scale_colors_by_normals(Point light)
{
  scale_colors_by_normals(light, *this);
}

void Unitdisk::scale_colors_by_normals(Point light, Unitdisk src_normals)
{
  scale_colors_by_either(light, src_normals.normals);
}

void Unitdisk::scale_colors_by_points(Point light, Unitdisk src_points)
{
  scale_colors_by_either(light, src_points.points);
}

void Unitdisk::scale_colors_by_either(Point light, Point *what)
{
  int t, r;
  int i;
  if (what == NULL) {
    return;
  }

  alloc_colors();
  for (r = 0; r <= rdivisions; r++) 
    colors[r] *= what[r].dot(light);
  i = rdivisions + 1;
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      colors[i] = colors[r];
      i++;
    }
  }
}  

void Unitdisk::set_colors(Color c)
{
  Color *dst;
  int i, npoints = get_npoints();
  alloc_colors();
  dst = colors;
  for (i = 0; i < npoints; i++, dst++) {
    dst->c[0] = c.c[0];
    dst->c[1] = c.c[1];
    dst->c[2] = c.c[2];
    dst->c[3] = c.c[3];
  }
}

void Unitdisk::add_colors(Color c)
{
  int i, npoints = get_npoints();
  alloc_colors();
  for (i = 0; i < npoints; i++) colors[i] += c;
}

void Unitdisk::free_colors()
{
  delete colors;
  colors = NULL;
  colors_size = 0;
}

inline float Unitdisk::area_triangle(Point a, Point b, Point c)
{
  return (float)(((a.pt[0]*b.pt[1] + b.pt[0]*c.pt[1] + c.pt[0]*a.pt[1]) -
	   (a.pt[1]*b.pt[0] + b.pt[1]*c.pt[0] + c.pt[1]*a.pt[0])) * .5);
}

inline float Unitdisk::area_triangle(GLfloat *a, GLfloat *b, 
				     GLfloat *c) 
{
  return (float)(((a[0]*b[1] + b[0]*c[1] + c[0]*a[1]) -
	   (a[1]*b[0] + b[1]*c[0] + c[1]*a[0])) * .5);
}

inline float Unitdisk::area_2triangle(GLfloat *a, GLfloat *b, 
				     GLfloat *c) 
{
  return ((a[0]*b[1] + b[0]*c[1] + c[0]*a[1]) -
	  (a[1]*b[0] + b[1]*c[0] + c[1]*a[0]));
}

void Unitdisk::scale_colors_by_darea(Unitdisk disk) 
{
  int pt1, pt2, pt3;
  int t, r, i;
  int npoints = get_npoints();
  float *rproducts1, *tproducts1, *rproducts2, *tproducts2;
  float area1, area2;

  rproducts1 = new float[npoints];
  tproducts1 = new float[npoints];
  rproducts2 = new float[npoints];
  tproducts2 = new float[npoints];

  // Рассчитываем формируемый сегмент, который создает диск
  // и позднее будет использоваться при расчете области
  i = 0;
  for (t = 0; t < tdivisions; t++) {
    for (r = 0; r < rdivisions; r++) {
      pt1 = i;
      pt2 = i + 1;
      rproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
		       points[pt1].pt[1]*points[pt2].pt[0]);
      rproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] - 
		       disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
      pt2 = ((t+1)%tdivisions)*(rdivisions + 1) + r;
      tproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
		       points[pt1].pt[1]*points[pt2].pt[0]);
      tproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] - 
		       disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
      i++;
    }
    pt1 = i;
    pt2 = ((t+1)%tdivisions)*(rdivisions + 1) + r;
    tproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
		     points[pt1].pt[1]*points[pt2].pt[0]);
    tproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] - 
		     disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
    i++;
  }

  // Рассчитываем область в центре диска
  area1 = area2 = 0.0;
  r = 1;
  for (t = 0; t <= tdivisions; t++) {
    pt1 = (t%tdivisions)*(rdivisions+1) + r;
    area1 += tproducts1[pt1];
    area2 += tproducts2[pt1];
  }
  if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
  for (t = 0; t < tdivisions; t++) {
    colors[t*(rdivisions+1)] *= area1;
  }

  for (t = 0; t < tdivisions; t++) {
    for (r = 1; r < rdivisions; r++) {
      pt1 = (t ? t-1 : tdivisions-1)*(rdivisions+1) + r - 1;
      pt3 = t*(rdivisions + 1) + r - 1;
      pt2 = ((t+1) % tdivisions)*(rdivisions+1) + r - 1;
      area1 = rproducts1[pt1] + rproducts1[pt1 + 1];
      area1 += tproducts1[pt1 + 2] + tproducts1[pt3 + 2];
      area1 -= rproducts1[pt2 + 1] + rproducts1[pt2];
      area1 -= tproducts1[pt3] + tproducts1[pt1];
      area2 = rproducts2[pt1] + rproducts2[pt1 + 1];
      area2 += tproducts2[pt1 + 2] + tproducts2[pt3 + 2];
      area2 -= rproducts2[pt2 + 1] + rproducts2[pt2];
      area2 -= tproducts2[pt3] + tproducts2[pt1];
      if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
      colors[pt3 + 1] *= area1;
    }
  }

  // Рассчитываем внешнюю область диска
  r = rdivisions;
  for (t = 0; t < tdivisions; t++) {
    pt1 = (t ? t-1 : tdivisions-1)*(rdivisions+1) + r - 1;
    pt3 = t*(rdivisions + 1) + r - 1;
    pt2 = ((t+1) % tdivisions)*(rdivisions+1) + r - 1;
    area1 = rproducts1[pt1];
    area1 += tproducts1[pt1 + 1] + tproducts1[pt3 + 1];
    area1 -= rproducts1[pt2];
    area1 -= tproducts1[pt1] + tproducts1[pt3];
    area2 = rproducts2[pt1];
    area2 += tproducts2[pt1 + 1] + tproducts2[pt3 + 1];
    area2 -= rproducts2[pt2];
    area2 -= tproducts2[pt1] + tproducts2[pt3];
    if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
    colors[pt3 + 1] *= area1;
  }


  delete rproducts1;
  delete tproducts1;
  delete rproducts2;
  delete tproducts2;
}

void Unitdisk::fill_trig_tables()
{
  int t;

  delete sintable;
  delete costable;
  sintable = new float[tdivisions];
  costable = new float[tdivisions];
  for (t = 0; t < tdivisions; t++) {
    costable[t] = (float)cos((double)(M_2PI * (float)t / (float)tdivisions));
    sintable[t] = (float)sin((double)(M_2PI * (float)t / (float)tdivisions));
  }
}
Соседние файлы в папке Lighting