
import java.io.*;

import javax.vecmath.*;

import org.bouncycastle.graphics.rt.*;
import org.bouncycastle.graphics.rt.util.*;
import org.bouncycastle.graphics.rt.tools.io.*;
import org.bouncycastle.graphics.rt.shaders.ct.TurbulenceShader;

/**
 * a solid wood shader
 */
public class SolidWood
    extends TurbulenceShader
{
    public SolidWood(
        World world)
    {
        super(world);
    }

    public static Shader getInstance(
        World         world,
        RMLReader       in)
        throws IOException
    {
        SolidWood w = new SolidWood(world);

        w.readObject(in);

        return w;
    }

    /**
     * the shade routine. If you want more than the below, this is
     * the method to override.
     */
    public void shade(
        Hit        hit,
        Pixel      p)
    {
        p.set(0, 0, 0, 1);

        base.set(hit.color());

        //
        // role in the "wood" function
        //
        // step 1. calculate our factor.
        //
        float       f;
        Point3f     loc = new Point3f(hit.oP());

        this.toShaderSpace(loc);

        f = (float)Math.sqrt(loc.x * loc.x + loc.y * loc.y);

        f += percentageTurbulence * generator.getNoise(loc);

        f = Util.triWave(f);
        f = clamp((1 + (f - squeeze) / (1 - squeeze)) / 2);

        //
        // step 2. mix in the blend color.
        //
        if (f > 0)
        { 
            if (colorMap == null)
            {
                base.interpolate(blendColor, f * blend);
            }
            else
            {
                base.interpolate(colorMap.colorAt(f), f * blend);
            }
        }

        p.scaledAdd(base, ambientContribution(world));

        LightDataList   contribs = hit.visibleLights(world);

        p.scaledAdd(base, diffuse, diffuseContribution(contribs, hit));

        if (isSpecular())
        {
            p.scaledAdd(specular, specularContribution(contribs, hit, roughness));
        }

        contribs.dispose();

        if (hit.level() >= maxLevel)
        {
            p.scale(alpha);

            return;
        }

        if (reflectionRoughness != 0)
        {
            hit.perturbN(reflectionRoughness);
        }

        //
        // fire off a reflection ray (if necessary).
        //
        if (isReflecting())
        {
            Ray           ir = hit.reflectedRay();
            Pixel         pix = new Pixel();

            ir.trace(world, pix);

            p.scaledAdd(reflectance, pix);

            ir.dispose();
        }

        p.scale(alpha);
    }
}

