Příklad k článku Java a 3D grafika - Geometry (Normálové vektory)

package interval.j3d;

import javax.media.j3d.*;
import javax.vecmath.*;
/*
* Tato třída slouží k vytváření "výškových map"
*/
public class HeightMap extends Shape3D{

//v poli heights jsou uloženy z-ové souřadnice, tj. "výšky" nad souřadnicemi
//xy, z-ová souřadnice bodu xy je na heights[y][x], heights[0][0] je levý dolní roh

  public HeightMap(float[][] heights, float velikostX, float velikostY) {
   int pocetBodu1 = heights.length;
   int pocetBodu2 = heights[0].length;
   //vytvoříme pole pro normálové vektory jednotlivých trojúhelníků,
   //jejich počet je (pocetBodu1-1)*(2*(pocetBodu2-1))
   //z těchto vektorů budeme později počítat průměr
   Vector3f normals[][] = new Vector3f[pocetBodu1-1][2*(pocetBodu2-1)];
   //vytvoříme pole trojúhelníků číslo 6*(pocetBodu1-1)*(pocetBodu2-1)
   //vyjadřuje počet vrcholů, tzn. každý vrchol je uložen hned několikrát,
   //což není příliš úsporné z hlediska využití paměti, ale v dalších článcích
   //si ukážeme lepší způsoby
   TriangleArray ta = new TriangleArray(6*(pocetBodu1-1)*(pocetBodu2-1), GeometryArray.COORDINATES | GeometryArray.NORMALS);
   //tady vypočítáme normálové vektory jednotlivých trojúhelníků
   for (int i = 0; i < pocetBodu1-1; i++){
    for (int j = 0; j < pocetBodu2-1; j++){
      //1. trojúhleník ve čtverci
      float a1 = 0;
      float a2 = -(float)velikostY/(float)(pocetBodu1-1);
      float a3 = heights[i][j]-heights[i+1][j];
      float b1 = (float)velikostX/(float)(pocetBodu2-1);
      float b2 = 0;
      float b3 = heights[i+1][j+1]-heights[i+1][j];
     
      //vektorový součin implementovaný pro změnu vlastními silami
      normals[i][2*j] = new Vector3f(a2*b3, a3*b1, -b1*a2);
     
      //2. trojúhleník ve čtverci
      a1 = 0;
      a2 = (float)velikostY/(float)(pocetBodu1-1);
      a3 = heights[i+1][j+1]-heights[i][j+1];
      b1 = -(float)velikostX/(float)(pocetBodu2-1);
      b2 = 0;
      b3 = heights[i][j]-heights[i][j+1];
     
      normals[i][2*j+1] = new Vector3f(a2*b3, a3*b1, -b1*a2);
    }
   }
   //v tomto poli už budou uloženy výsledné normálové vektory spočítané
   //jako průměr normálových vektorů sousedících s daným vrcholem
   Vector3f temp_normals[][] = new Vector3f[pocetBodu1][pocetBodu2];
   //tady počítáme průměry z normálových vektorů pro vrcholy neležící "na kraji" pole
   for (int i = 1; i < pocetBodu1-1; i++){
    for (int j = 1; j < pocetBodu2-1; j++){
      Vector3f v1 = new Vector3f();
      v1.add(normals[i-1] [2*j-2]);
      v1.add(normals[i-1] [2*j-1]);
      v1.add(normals[i-1] [2*j]);
      v1.add(normals[i] [2*j-1]);
      v1.add(normals[i] [2*j]);
      v1.add(normals[i] [2*j+1]);
      //nesmíme zapomenout znormalizovat výsledek
      v1.normalize();
      temp_normals[i][j] = v1;
    }
   }
   //tady počítáme průměry z normálových vektorů pro vrcholy, které leží ve "spodní řadě"
   for (int j = 1; j < pocetBodu2-1; j++){
    int i = 0;
    Vector3f v1 = new Vector3f();
    v1.add(normals[i][2*j-1]);
    v1.add(normals[i][2*j]);
    v1.add(normals[i][2*j+1]);
    v1.normalize();
    temp_normals[0][j] = v1;
   }
   //tady počítáme průměry z normálových vektorů pro vrcholy, které leží v "horní řadě"
   for (int j = 1; j < pocetBodu2-1; j++){
    int i = pocetBodu1-1;
    Vector3f v1 = new Vector3f();
    v1.add(normals[pocetBodu1-2][2*j-2]);
    v1.add(normals[pocetBodu1-2][2*j-1]);
    v1.add(normals[pocetBodu1-2][2*j]);
    v1.normalize();
    temp_normals[i][j] = v1;
   }
   //tady počítáme průměry z normálových vektorů pro vrcholy, které leží v "levém sloupci"
   for (int i = 1; i < pocetBodu1-1; i++){
    int j = 0;
    Vector3f v1 = new Vector3f();
    v1.add(normals[i-1][0]);
    v1.add(normals[i][0]);
    v1.add(normals[i][1]);
    v1.normalize();
    temp_normals[i][j] = v1;
   }
   //tady počítáme průměry z normálových vektorů pro vrcholy, které leží v "pravém sloupci"
   for (int i = 1; i < pocetBodu1-1; i++){
    int j = pocetBodu2-1;
    Vector3f v1 = new Vector3f();
    v1.add(normals[i-1][2*pocetBodu2-3]);
    v1.add(normals[i-1][2*pocetBodu2-4]);
    v1.add(normals[i][2*pocetBodu2-3]);
    v1.normalize();
    temp_normals[i][j] = v1;
   }
   //normálové vrcholy vrcholů ležících "v rozích" spočítáme až tady
   normals[pocetBodu1-2][2*(pocetBodu2-1)-1].normalize();
   normals[0][0].normalize();
   normals[pocetBodu1-2][0].normalize();
   normals[0][2*(pocetBodu2-1)-1].normalize();
   temp_normals[pocetBodu1-1][pocetBodu2-1] = normals[pocetBodu1-2][2*(pocetBodu2-1)-1];
   temp_normals[0][0] = normals[0][0];
   temp_normals[pocetBodu1-1][0] = normals[pocetBodu1-2][0];
   temp_normals[0][pocetBodu2-1] = normals[0][2*(pocetBodu2-1)-1];
   normals = null;
   float temp[] = new float[3];
   //zde vkládáme jednotlivé vrcholy a jejich normálové vektory do objektu TriangleArray
   for (int i = 0; i < pocetBodu1-1; i++){
    for (int j = 0; j < pocetBodu2-1; j++){
     
      temp[0] = j*velikostX/(float)pocetBodu2;
      temp[1] = (i+1)*velikostY/(float)pocetBodu1;
      temp[2] = heights[i+1][j];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j+1, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j+1, temp_normals[i+1][j]);
     
      temp[0] = j*velikostX/(float)pocetBodu2;
      temp[1] = i*velikostY/(float)pocetBodu1;
      temp[2] = heights[i][j];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j+2, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j+2, temp_normals[i][j]);
     
      temp[0] = (j+1)*velikostX/(float)pocetBodu2;
      temp[1] = (i+1)*velikostY/(float)pocetBodu1;
      temp[2] = heights[i+1][j+1];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j, temp_normals[i+1][j+1]);
     
      temp[0] = (j+1)*velikostX/(float)pocetBodu2;
      temp[1] = (i+1)*velikostY/(float)pocetBodu1;
      temp[2] = heights[i+1][j+1];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j+3, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j+3, temp_normals[i+1][j+1]);
     
      temp[0] = j*velikostX/(float)pocetBodu2;
      temp[1] = i*velikostY/(float)pocetBodu1;
      temp[2] = heights[i][j];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j+4, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j+4, temp_normals[i][j]);
     
      temp[0] = (j+1)*velikostX/(float)pocetBodu2;
      temp[1] = i*velikostY/(float)pocetBodu1;
      temp[2] = heights[i][j+1];
      ta.setCoordinate(i*6*(pocetBodu2-1)+6*j+5, temp);
      ta.setNormal(i*6*(pocetBodu2-1)+6*j+5, temp_normals[i][j+1]);
    }
   }
   setGeometry(ta);
  }
}