float width45 = Math.abs(6.0f * bumpHeight);
boolean invertBumps = bumpHeight < 0;
Vector3f position = new Vector3f(0.0f, 0.0f, 0.0f);
Vector3f viewpoint = new Vector3f(width / 2.0f, height / 2.0f, viewDistance);
Vector3f normal = new Vector3f();
Color4f envColor = new Color4f();
Color4f diffuseColor = new Color4f( new Color(material.diffuseColor) );
Color4f specularColor = new Color4f( new Color(material.specularColor) );
Function2D bump = bumpFunction;
// Apply the bump softness
if (bumpSource == BUMPS_FROM_IMAGE || bumpSource == BUMPS_FROM_IMAGE_ALPHA || bumpSource == BUMPS_FROM_MAP || bump == null) {
if ( bumpSoftness != 0 ) {
int bumpWidth = width;
int bumpHeight = height;
int[] bumpPixels = inPixels;
if ( bumpSource == BUMPS_FROM_MAP && bumpFunction instanceof ImageFunction2D ) {
ImageFunction2D if2d = (ImageFunction2D)bumpFunction;
bumpWidth = if2d.getWidth();
bumpHeight = if2d.getHeight();
bumpPixels = if2d.getPixels();
}
int [] tmpPixels = new int[bumpWidth * bumpHeight];
int [] softPixels = new int[bumpWidth * bumpHeight];
/*
for (int i = 0; i < 3; i++ ) {
BoxBlurFilter.blur( bumpPixels, tmpPixels, bumpWidth, bumpHeight, (int)bumpSoftness );
BoxBlurFilter.blur( tmpPixels, softPixels, bumpHeight, bumpWidth, (int)bumpSoftness );
}
*/
Kernel kernel = GaussianFilter.makeKernel( bumpSoftness );
GaussianFilter.convolveAndTranspose( kernel, bumpPixels, tmpPixels, bumpWidth, bumpHeight, true, false, false, GaussianFilter.WRAP_EDGES );
GaussianFilter.convolveAndTranspose( kernel, tmpPixels, softPixels, bumpHeight, bumpWidth, true, false, false, GaussianFilter.WRAP_EDGES );
bump = new ImageFunction2D(softPixels, bumpWidth, bumpHeight, ImageFunction2D.CLAMP, bumpSource == BUMPS_FROM_IMAGE_ALPHA);
final Function2D bbump = bump;
if ( bumpShape != 0 ) {
bump = new Function2D() {
private Function2D original = bbump;
public float evaluate(float x, float y) {
float v = original.evaluate( x, y );
switch ( bumpShape ) {
case 1:
// v = v > 0.5f ? 0.5f : v;
v *= ImageMath.smoothStep( 0.45f, 0.55f, v );
break;
case 2:
v = v < 0.5f ? 0.5f : v;
break;
case 3:
v = ImageMath.triangle( v );
break;
case 4:
v = ImageMath.circleDown( v );
break;
case 5:
v = ImageMath.gain( v, 0.75f );
break;
}
return v;
}
};
}
} else if ( bumpSource != BUMPS_FROM_MAP )
bump = new ImageFunction2D(inPixels, width, height, ImageFunction2D.CLAMP, bumpSource == BUMPS_FROM_IMAGE_ALPHA);
}
float reflectivity = material.reflectivity;
float areflectivity = (1-reflectivity);
Vector3f v1 = new Vector3f();
Vector3f v2 = new Vector3f();
Vector3f n = new Vector3f();
Light[] lightsArray = new Light[lights.size()];
lights.copyInto(lightsArray);
for (int i = 0; i < lightsArray.length; i++)
lightsArray[i].prepare(width, height);
float[][] heightWindow = new float[3][width];
for (int x = 0; x < width; x++)
heightWindow[1][x] = width45*bump.evaluate(x, 0);
// Loop through each source pixel
for (int y = 0; y < height; y++) {
boolean y0 = y > 0;
boolean y1 = y < height-1;
position.y = y;
for (int x = 0; x < width; x++)
heightWindow[2][x] = width45*bump.evaluate(x, y+1);
for (int x = 0; x < width; x++) {
boolean x0 = x > 0;
boolean x1 = x < width-1;
// Calculate the normal at this point
if (bumpSource != BUMPS_FROM_BEVEL) {
// Complicated and slower method
// Calculate four normals using the gradients in +/- X/Y directions
int count = 0;
normal.x = normal.y = normal.z = 0;
float m0 = heightWindow[1][x];
float m1 = x0 ? heightWindow[1][x-1]-m0 : 0;
float m2 = y0 ? heightWindow[0][x]-m0 : 0;
float m3 = x1 ? heightWindow[1][x+1]-m0 : 0;
float m4 = y1 ? heightWindow[2][x]-m0 : 0;
if (x0 && y1) {
v1.x = -1.0f; v1.y = 0.0f; v1.z = m1;
v2.x = 0.0f; v2.y = 1.0f; v2.z = m4;
n.cross(v1, v2);
n.normalize();
if (n.z < 0.0)
n.z = -n.z;
normal.add(n);
count++;
}
if (x0 && y0) {
v1.x = -1.0f; v1.y = 0.0f; v1.z = m1;
v2.x = 0.0f; v2.y = -1.0f; v2.z = m2;
n.cross(v1, v2);
n.normalize();
if (n.z < 0.0)
n.z = -n.z;
normal.add(n);
count++;
}
if (y0 && x1) {
v1.x = 0.0f; v1.y = -1.0f; v1.z = m2;
v2.x = 1.0f; v2.y = 0.0f; v2.z = m3;
n.cross(v1, v2);
n.normalize();
if (n.z < 0.0)
n.z = -n.z;
normal.add(n);
count++;
}
if (x1 && y1) {
v1.x = 1.0f; v1.y = 0.0f; v1.z = m3;
v2.x = 0.0f; v2.y = 1.0f; v2.z = m4;
n.cross(v1, v2);
n.normalize();
if (n.z < 0.0)
n.z = -n.z;
normal.add(n);
count++;
}
// Average the four normals
normal.x /= count;
normal.y /= count;
normal.z /= count;
}
if (invertBumps) {
normal.x = -normal.x;
normal.y = -normal.y;
}
position.x = x;
if (normal.z >= 0) {
// Get the material colour at this point
if (colorSource == COLORS_FROM_IMAGE)
setFromRGB(diffuseColor, inPixels[index]);
else
setFromRGB(diffuseColor, material.diffuseColor);
if (reflectivity != 0 && environmentMap != null) {
//FIXME-too much normalizing going on here
tmpv2.set(viewpoint);
tmpv2.sub(position);
tmpv2.normalize();
tmpv.set(normal);
tmpv.normalize();
// Reflect
tmpv.scale( 2.0f*tmpv.dot(tmpv2) );
tmpv.sub(v);
tmpv.normalize();
setFromRGB(envColor, getEnvironmentMap(tmpv, inPixels, width, height));//FIXME-interpolate()
diffuseColor.x = reflectivity*envColor.x + areflectivity*diffuseColor.x;
diffuseColor.y = reflectivity*envColor.y + areflectivity*diffuseColor.y;
diffuseColor.z = reflectivity*envColor.z + areflectivity*diffuseColor.z;
}
// Shade the pixel
Color4f c = phongShade(position, viewpoint, normal, diffuseColor, specularColor, material, lightsArray);
int alpha = inPixels[index] & 0xff000000;
int rgb = ((int)(c.x * 255) << 16) | ((int)(c.y * 255) << 8) | (int)(c.z * 255);
outPixels[index++] = alpha | rgb;
} else
outPixels[index++] = 0;