/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2011 Andreas Maschke
This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This software 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this software;
if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jwildfire.transform;
import org.jwildfire.image.SimpleImage;
import org.jwildfire.transform.Mesh3DTransformer.Faces;
public class Phong3DRenderer extends Mesh3DRenderer {
private static final int NMAX = 256;
// TODO : Faces.MIRRORED currently not supported
@Override
public void renderImage(Mesh3D pMesh3D, Mesh3DTransformer pMesh3DTransformer, SimpleImage pImg) {
init(pMesh3D, pMesh3DTransformer, pImg);
double nx[] = new double[f3Count];
double ny[] = new double[f3Count];
double nz[] = new double[f3Count];
double perpx[] = new double[f3Count];
double perpy[] = new double[f3Count];
double perpz[] = new double[f3Count];
double baseDelta[] = new double[f3Count];
int taxis[] = new int[f3Count];
int nb[] = new int[f3Count];
int pf[] = new int[f3Count];
double nbx1[] = new double[f3Count];
double nby1[] = new double[f3Count];
double nbz1[] = new double[f3Count];
double nbx2[] = new double[f3Count];
double nby2[] = new double[f3Count];
double nbz2[] = new double[f3Count];
double nbx3[] = new double[f3Count];
double nby3[] = new double[f3Count];
double nbz3[] = new double[f3Count];
double pmaxangle = Math.cos(pMesh3DTransformer.getPhongAngle() * Math.PI / 180.0);
/* calc the normals and baselines */
{
double vcx, vcy, vcz, tx, ty, tz;
double n1x, n1y, n1z, rr1, n2x, n2y, n2z, rr2, proj, udom;
for (int i = 0; i < f3Count; i++) {
int p1 = p31[i];
int p2 = p32[i];
int p3 = p33[i];
double x1 = cx + x[p1];
double y1 = cy + y[p1];
double z1 = z[p1];
double x2 = cx + x[p2];
double y2 = cy + y[p2];
double z2 = z[p2];
double x3 = cx + x[p3];
double y3 = cy + y[p3];
double z3 = z[p3];
/* normals */
double vax = x1 - x2;
double vay = y1 - y2;
double vaz = z1 - z2;
double vbx = x3 - x2;
double vby = y3 - y2;
double vbz = z3 - z2;
vcx = x1 - x3;
vcy = y1 - y3;
vcz = z1 - z3;
n1x = vay * vbz - vaz * vby;
n1y = vaz * vbx - vax * vbz;
n1z = vax * vby - vay * vbx;
rr1 = n1x * n1x + n1y * n1y + n1z * n1z;
n2x = vby * vcz - vbz * vcy;
n2y = vbz * vcx - vbx * vcz;
n2z = vbx * vcy - vby * vcx;
rr2 = n2x * n2x + n2y * n2y + n2z * n2z;
if (rr1 > rr2) {
rr1 = Math.sqrt(rr1);
if (rr1 < PZERO)
rr1 = PZERO;
nx[i] = n1x / rr1;
ny[i] = n1y / rr1;
nz[i] = n1z / rr1;
}
else {
rr2 = Math.sqrt(rr2);
if (rr2 < PZERO)
rr2 = PZERO;
nx[i] = 0.0 - n2x / rr2;
ny[i] = 0.0 - n2y / rr2;
nz[i] = 0.0 - n2z / rr2;
}
/* default axis */
tx = Math.abs(vbx);
ty = Math.abs(vby);
tz = Math.abs(vbz);
switch (max3(tx, ty, tz)) {
case 1:
taxis[i] = 1;
baseDelta[i] = vbx;
break;
case 2:
taxis[i] = 2;
baseDelta[i] = vby;
break;
default:
taxis[i] = 3;
baseDelta[i] = vbz;
}
/* vperp */
vax = x2 - x3;
vay = y2 - y3;
vaz = z2 - z3;
rr1 = Math.sqrt(vax * vax + vay * vay + vaz * vaz);
if (rr1 < PZERO)
rr1 = 1.0;
vax /= rr1;
vay /= rr1;
vaz /= rr1;
vbx = x1 - x3;
vby = y1 - y3;
vbz = z1 - z3;
proj = vbx * vax + vby * vay + vbz * vaz;
vax *= proj;
vay *= proj;
vaz *= proj;
vcx = vax - vbx;
vcy = vay - vby;
vcz = vaz - vbz;
rr1 = Math.sqrt(vcx * vcx + vcy * vcy + vcz * vcz);
if (rr1 < PZERO)
rr1 = 1.0;
vcx /= rr1;
vcy /= rr1;
vcz /= rr1;
udom = vbx * vcx + vby * vcy + vbz * vcz;
if (udom < PZERO)
udom = 1;
udom = 0.0 - udom;
perpx[i] = vcx / udom;
perpy[i] = vcy / udom;
perpz[i] = vcz / udom;
}
}
/* search the valid point-range */
/* if((obj->f4Count==0) && (obj->f2Count==0) && (obj->f1Count==0) && (obj->sf3Count==0)) {*/
int pMin = 0;
int pMax = pMesh3D.getPCount();
/* }
else {
ULONG pp1,i;
pMin=pCount-1;pMax=0;
for(i=0;i<f3Count;i++) {
pp1=p31[i];
if(pp1<pMin) pMin=pp1;
if(pp1>pMax) pMax=pp1;
pp1=p32[i];
if(pp1<pMin) pMin=pp1;
if(pp1>pMax) pMax=pp1;
pp1=p33[i];
if(pp1<pMin) pMin=pp1;
if(pp1>pMax) pMax=pp1;
}
pMax++;
}
*/
double phongAngle = pMesh3DTransformer.getPhongAngle();
if ((phongAngle <= 0.0) || (phongAngle > 180.0)) {
for (int i = pMin; i < pMax; i++) {
int pcurr = 0;
for (int bs = 0; bs < f3Count; bs++) {
int pp1 = p31[bs];
int pp2 = p32[bs];
int pp3 = p33[bs];
if (i == pp1) {
pf[pcurr] = 1;
nb[pcurr] = bs;
pcurr++;
}
if (i == pp2) {
pf[pcurr] = 2;
nb[pcurr] = bs;
pcurr++;
}
if (i == pp3) {
pf[pcurr] = 3;
nb[pcurr] = bs;
pcurr++;
}
if (pcurr >= NMAX)
break;
}
if (pcurr > 0) {
double tnx = 0.0, tny = 0.0, tnz = 0.0;
for (int bs = 0; bs < pcurr; bs++) {
int nn = nb[bs];
tnx += nx[nn];
tny += ny[nn];
tnz += nz[nn];
}
rr = Math.sqrt(tnx * tnx + tny * tny + tnz * tnz);
if (rr < PZERO) {/*printf("bad normal vector\n");*/
rr = 1.0;
}
tnx /= rr;
tny /= rr;
tnz /= rr;
for (int bs = 0; bs < pcurr; bs++) {
int nn = nb[bs];
switch (pf[bs]) {
case 1:
nbx1[nn] = tnx;
nby1[nn] = tny;
nbz1[nn] = tnz;
break;
case 2:
nbx2[nn] = tnx;
nby2[nn] = tny;
nbz2[nn] = tnz;
break;
default:
nbx3[nn] = tnx;
nby3[nn] = tny;
nbz3[nn] = tnz;
}
}
}
}
}
else {
for (int i = pMin; i < pMax; i++) {
// System.out.println(i + "/" + pMax);
int pcurr = 0;
for (int bs = 0; bs < f3Count; bs++) {
int pp1 = p31[bs];
int pp2 = p32[bs];
int pp3 = p33[bs];
if (i == pp1) {
pf[pcurr] = 1;
nb[pcurr] = bs;
pcurr++;
}
if (i == pp2) {
pf[pcurr] = 2;
nb[pcurr] = bs;
pcurr++;
}
if (i == pp3) {
pf[pcurr] = 3;
nb[pcurr] = bs;
pcurr++;
}
if (pcurr >= NMAX)
break;
}
if (pcurr > 0) {
for (int bs = 0; bs < pcurr; bs++) {
int nn = nb[bs];
double lnx = nx[nn];
double lny = ny[nn];
double lnz = nz[nn];
double tnx = 0.0, tny = 0.0, tnz = 0.0;
for (int o = 0; o < pcurr; o++) {
int mm = nb[o];
double lmx = nx[mm];
double lmy = ny[mm];
double lmz = nz[mm];
if (o != bs) {
double pangle = lnx * lmx + lny * lmy + lnz * lmz;
if (pangle > pmaxangle) {
tnx += lmx;
tny += lmy;
tnz += lmz;
}
}
else {
tnx += lmx;
tny += lmy;
tnz += lmz;
}
}
rr = Math.sqrt(tnx * tnx + tny * tny + tnz * tnz);
if (rr < PZERO) {
tnx = lnx;
tny = lny;
tnz = lnz;
}
else {
tnx /= rr;
tny /= rr;
tnz /= rr;
}
switch (pf[bs]) {
case 1:
nbx1[nn] = tnx;
nby1[nn] = tny;
nbz1[nn] = tnz;
break;
case 2:
nbx2[nn] = tnx;
nby2[nn] = tny;
nbz2[nn] = tnz;
break;
default:
nbx3[nn] = tnx;
nby3[nn] = tny;
nbz3[nn] = tnz;
}
}
}
}
}
/* render it */
for (int i = 0; i < f3Count; i++) {
int p1 = p31[i];
int p2 = p32[i];
int p3 = p33[i];
fZSortArray[i] = 0.0 - (z[p1] + z[p2] + z[p3]) * 0.33;
fInd[i] = i;
}
heapSortFloat1(fZSortArray, fInd, f3Count);
for (int i = 0; i < f3Count; i++) {
int p1 = p31[fInd[i]];
int p2 = p32[fInd[i]];
int p3 = p33[fInd[i]];
double x1 = cx + x[p1];
double y1 = cy + y[p1];
double z1 = z[p1];
double x2 = cx + x[p2];
double y2 = cy + y[p2];
double z2 = z[p2];
double x3 = cx + x[p3];
double y3 = cy + y[p3];
double z3 = z[p3];
lux = (int) (x1 + 0.5);
luy = (int) (y1 + 0.5);
luz = (int) (z1 + 0.5);
rux = (int) (x2 + 0.5);
ruy = (int) (y2 + 0.5);
ruz = (int) (z2 + 0.5);
rbx = (int) (x3 + 0.5);
rby = (int) (y3 + 0.5);
rbz = (int) (z3 + 0.5);
cosa = 0.0 - nz[fInd[i]];
int swapCosA = 0;
if ((faces == Faces.DOUBLE) && (cosa >= 0.0)) {
cosa = 0.0 - cosa;
swapCosA = 1;
}
if (cosa < 0.0) {
if (u != null) {
int px = (int) (u[p1] * texture.getImageWidth() + 0.5);
int py = (int) (v[p1] * texture.getImageHeight() + 0.5);
toolPixel.setARGBValue(texture.getARGBValueIgnoreBounds(px, py));
}
else {
toolPixel.setARGBValue(coloro[fInd[i]]);
}
r = toolPixel.r;
g = toolPixel.g;
b = toolPixel.b;
nfx = nx[fInd[i]];
nfy = ny[fInd[i]];
nfz = nz[fInd[i]];
double fperpx = perpx[fInd[i]];
double fperpy = perpy[fInd[i]];
double fperpz = perpz[fInd[i]];
double n1x = nbx1[fInd[i]];
double n1y = nby1[fInd[i]];
double n1z = nbz1[fInd[i]];
double n2x = nbx2[fInd[i]];
double n2y = nby2[fInd[i]];
double n2z = nbz2[fInd[i]];
double n3x = nbx3[fInd[i]];
double n3y = nby3[fInd[i]];
double n3z = nbz3[fInd[i]];
/* fill the transformed triangle */
/* create the edges */
/* 1->2 */
if (p1 < p2) {
n12 = bresenham3D(lux, luy, luz, rux, ruy, ruz, x12, y12, z12);
}
else {
n12 = bresenham3D(rux, ruy, ruz, lux, luy, luz, x12, y12, z12);
}
/* 2->3 */
if (p2 < p3) {
n23 = bresenham3D(rux, ruy, ruz, rbx, rby, rbz, x23, y23, z23);
}
else {
n23 = bresenham3D(rbx, rby, rbz, rux, ruy, ruz, x23, y23, z23);
}
/* 3->1 */
if (p3 < p1) {
n41 = bresenham3D(rbx, rby, rbz, lux, luy, luz, x41, y41, z41);
}
else {
n41 = bresenham3D(lux, luy, luz, rbx, rby, rbz, x41, y41, z41);
}
/* create the bounding box */
int xmin, xmax;
xmin = xmax = lux;
if (rux < xmin)
xmin = rux;
else if (rux > xmax)
xmax = rux;
if (rbx < xmin)
xmin = rbx;
else if (rbx > xmax)
xmax = rbx;
int ymin, ymax;
ymin = ymax = luy;
if (ruy < ymin)
ymin = ruy;
else if (ruy > ymax)
ymax = ruy;
if (rby < ymin)
ymin = rby;
else if (rby > ymax)
ymax = rby;
/* fill the area */
for (k = ymin; k <= ymax; k++) {
if ((k >= 0) && (k < height)) {
rFill3();
int zz = zMin;
double zs;
if (min != max)
zs = (double) (zMax - zMin) / (double) (max - min);
else
zs = 0.0;
for (int l = min; l <= max; l++) {
if ((l >= 0) && (l < width)) {
/* compute the color */
double xx = (double) l;
double yy = (double) k;
double vpx = xx - x1;
double vpy = yy - y1;
double vpz = zz - z1;
double vpx2 = x3 - x1;
double vpy2 = y3 - y1;
double vpz2 = z3 - z1;
double u = vpx * fperpx + vpy * fperpy + vpz * fperpz;
double u2 = vpx2 * fperpx + vpy2 * fperpy + vpz2 * fperpz;
if (Math.abs(u2) < PZERO)
u2 = 1.0;
u /= u2;
double v;
switch (taxis[fInd[i]]) {
case 1:
v = (vpx / u - (x2 - x1)) / (baseDelta[fInd[i]]);
break;
case 2:
v = (vpy / u - (y2 - y1)) / (baseDelta[fInd[i]]);
break;
default:
v = (vpz / u - (z2 - z1)) / (baseDelta[fInd[i]]);
}
double ntempx1 = u * (n2x - n1x) + n1x;
double ntempy1 = u * (n2y - n1y) + n1y;
double ntempz1 = u * (n2z - n1z) + n1z;
double ntempx2 = u * (n3x - n1x) + n1x;
double ntempy2 = u * (n3y - n1y) + n1y;
double ntempz2 = u * (n3z - n1z) + n1z;
nfx = v * (ntempx2 - ntempx1) + ntempx1;
nfy = v * (ntempy2 - ntempy1) + ntempy1;
nfz = v * (ntempz2 - ntempz1) + ntempz1;
rr = Math.sqrt(nfx * nfx + nfy * nfy + nfz * nfz);
if (rr < PZERO)
rr = 1.0;
nfx /= rr;
nfy /= rr;
nfz /= rr;
if (swapCosA != 0) {
nfx = 0.0 - nfx;
nfy = 0.0 - nfy;
nfz = 0.0 - nfz;
}
reflectViewVector();
addLight(xx, yy, zz);
pImg.setRGB(l, k, pr, pg, pb);
}
zz += zs;
}
}
} /* k loop */
} /* cosa */
} /* loop */
}
private int max3(double x, double y, double z) {
return ((x > y) ? ((x > z) ? 1 : 3) : ((y > z) ? 2 : 3));
}
}