C3DRenderingEngine.java
/* 
 * ################################################################
 
 * ProActive: The Java(TM) library for Parallel, Distributed, 
 *            Concurrent computing with Security and Mobility
 
 * Copyright (C) 1997-2007 INRIA/University of Nice-Sophia Antipolis
 * Contact: proactive@objectweb.org
 
 * This library 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 any later version.
 *  
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *  
 *  Initial developer(s):               The ProActive Team
 *                        http://www.inria.fr/oasis/ProActive/contacts.html
 *  Contributor(s): 
 
 * ################################################################
 */ 
package org.objectweb.proactive.examples.c3d;

import org.objectweb.proactive.examples.c3d.geom.Ray;
import org.objectweb.proactive.examples.c3d.geom.Scene;
import org.objectweb.proactive.examples.c3d.geom.Vec;
import org.objectweb.proactive.examples.c3d.prim.Isect;
import org.objectweb.proactive.examples.c3d.prim.Light;
import org.objectweb.proactive.examples.c3d.prim.Surface;
import org.objectweb.proactive.examples.c3d.prim.View;


/**
 * Rendering Engine used by C3D.
 * When fed a scene and an Interval, returns a 2D picture of the 3D scene.
 */
public class C3DRenderingEngine implements java.io.Serializable,
    RenderingEngine {
    private static final double INFINITE = 1e6;

    // Alpha channel
    private static final int alpha = 255 << 24;

    // the scene which should be drawn
    private Scene scene;

    // speed up contraptions
    // Current intersection instance (only one is needed!)
    private Isect inter = new Isect();

    // replace new Vec(0,0,0)
    private final Vec voidVec = new Vec();

    //Temporary ray and Vec
    private Ray tRay = new Ray();
    private Vec tmpVec = new Vec();

    // used by toString to show a better value than C3DRenderingEngine@11d2572 
    protected String name = "name not set";

    /** Default constructor needed by ProActive */
    public C3DRenderingEngine() {
    }

    /** Constructor sets the value used by toString()*/
    public C3DRenderingEngine(String name) {
        this.name = name;
    }

    /** Creates the local objects used in the rendering  */
    public void setScene(Scene scene) {
        this.scene = scene;
    }

    /** Trace and send back the result to the dispatcher. <i>Heavily optimized!!!</i>
     @return the partial Image that was asked for */
    public Image2D render(int engineNb, Interval interval) {
        int[] row = new int[interval.totalImageWidth * (interval.yto -
            interval.yfrom)];
        int pixCounter = 0//iterator

        // Rendering variables
        int x;

        // Rendering variables
        int y;
        int red;
        int green;
        int blue;
        double xlen;
        double ylen;

        View view = scene.getView();
        Vec viewVec = Vec.sub(view.at, view.from);
        viewVec.normalize();

        Vec tmpVec = new Vec(viewVec);
        tmpVec.scale(Vec.dot(view.up, viewVec));

        Vec upVec = Vec.sub(view.up, tmpVec);
        upVec.normalize();

        Vec leftVec = Vec.cross(view.up, viewVec);
        leftVec.normalize();

        double frustrumwidth = view.dist * Math.tan(view.angle);

        upVec.scale(-frustrumwidth);
        leftVec.scale(view.aspect * frustrumwidth);

        Ray r = new Ray(view.from, this.voidVec);
        Vec col = new Vec();

        // All loops are reversed for 'speedup' (cf. thinking in java p331)
        // For each line
        for (y = interval.yfrom; y < interval.yto; y++) {
            ylen = ((2.0 * y/ interval.totalImageWidth1.0;

            // For each pixel of the line
            for (x = 0; x < interval.totalImageWidth; x++) {
                xlen = ((2.0 * x/ interval.totalImageWidth1.0;
                r.D = Vec.comb(xlen, leftVec, ylen, upVec);
                r.D.add(viewVec);
                r.D.normalize();
                col = trace(01.0, r);

                // computes the color of the ray
                red = (int) (col.x * 255.0);
                if (red > 255) {
                    red = 255;
                }
                green = (int) (col.y * 255.0);
                if (green > 255) {
                    green = 255;
                }
                blue = (int) (col.z * 255.0);
                if (blue > 255) {
                    blue = 255;
                }

                // Sets the pixels
                row[pixCounter++= C3DRenderingEngine.alpha | (red << 16|
                    (green << 8(blue);
            // end for (x)
        // end for (y)

        // sends the result back to the dispatcher
        return (new Image2D(row, interval, engineNb));
    }

    /** Find closest Ray, return initialized Isect with intersection information.
     * @returns true if intersection is found */
    private boolean intersect(Ray r, double maxt) {
        int nhits = 0;
        this.inter.t = maxt;
        int nbprimitives = this.scene.getNbPrimitives();
        for (int i = 0; i < nbprimitives; i++) {
            // uses global temporary Prim (tp) as temp.object for speedup
            Isect tp = this.scene.getPrimitive(i).intersect(r);
            if ((tp != null&& (tp.t < this.inter.t)) {
                // Fixme : this is a clone, no ? Hide this in Isect.java 
                this.inter.t = tp.t;
                this.inter.prim = tp.prim;
                this.inter.enter = tp.enter;
                nhits++;
            }
        }
        return nhits > 0;
    }

    /** Checks if there is a shadow  */
    private boolean shadow(Ray r, double tmax) {
        return !intersect(r, tmax);
    }

    /** Return the Vector's reflection direction  */
    private Vec specularDirection(Vec I, Vec N) {
        Vec r;
        r = Vec.comb(1.0 / Math.abs(Vec.dot(I, N)), I, 2.0, N);
        r.normalize();
        return r;
    }

    /** Return the Vector's transmission direction */
    private Vec transDir(Surface m1, Surface m2, Vec I, Vec N) {
        double n1 = (m1 == null1.0 : m1.ior;
        double n2 = (m2 == null1.0 : m2.ior;
        double eta = n1 / n2;
        double c1 = -Vec.dot(I, N);
        double cs2 = 1.0 (eta * eta * (1.0 (c1 * c1)));
        if (cs2 < 0.0) {
            return null;
        }
        Vec r = Vec.comb(eta, I, (eta * c1- Math.sqrt(cs2), N);
        r.normalize();
        return r;
    }

    /** Returns the shaded color */
    private Vec shade(int level, double weight, Vec P, Vec N, Vec I, Isect hit) {
        Vec tcol;
        Vec R;
        double diff;
        double spec;
        Surface surf;
        Vec col;
        int l;

        col = new Vec();
        surf = hit.prim.getSurface();
        R = new Vec();
        if (surf.shine > 1e-6) {
            R = specularDirection(I, N);
        }

        // Computes the effectof each light
        int nblights = this.scene.getNbLights();
        for (l = 0; l < nblights; l++) {
            Light light = this.scene.getLight(l);
            this.tmpVec.sub2(light.pos, P);
            if (Vec.dot(N, this.tmpVec>= 0.0) {
                this.tmpVec.normalize();

                this.tRay.P = P;
                this.tRay.D = this.tmpVec;

                // Checks if there is a shadow
                if (shadow(this.tRay, C3DRenderingEngine.INFINITE)) {
                    diff = Vec.dot(N, this.tmpVec* surf.kd * light.brightness;

                    col.adds(diff, surf.color);
                    if (surf.shine > 1e-6) {
                        spec = Vec.dot(R, this.tmpVec);
                        if (spec > 1e-6) {
                            spec = Math.pow(spec, surf.shine);
                            col.x += spec;
                            col.y += spec;
                            col.z += spec;
                        }
                    }
                }
            // if
        // for

        this.tRay.P = P;
        if ((surf.ks * weight1e-3) {
            this.tRay.D = specularDirection(I, N);
            tcol = trace(level + 1, surf.ks * weight, this.tRay);
            col.adds(surf.ks, tcol);
        }
        if ((surf.kt * weight1e-3) {
            if (hit.enter) {
                this.tRay.D = transDir(null, surf, I, N);
            else {
                this.tRay.D = transDir(surf, null, I, N);
            }
            tcol = trace(level + 1, surf.kt * weight, this.tRay);
            col.adds(surf.kt, tcol);
        }

        // garbaging...
        tcol = null;
        surf = null;

        return col;
    }

    /** Launches a ray. Please note, as return may be this.voidVec,
     * the return value should not be modified, once returned! */
    private Vec trace(int level, double weight, Ray ray) {
        Vec P;
        Vec N;
        boolean hit;

        // Checks the recursion level
        if (level > 6) {
            return new Vec();
        }

        hit = intersect(ray, C3DRenderingEngine.INFINITE);
        if (hit) {
            P = ray.point(this.inter.t);
            N = this.inter.prim.normal(P);
            if (Vec.dot(ray.D, N>= 0.0) {
                N.negate();
            }
            return shade(level, weight, P, N, ray.D, this.inter);
        }

        // no intersection --> col = 0,0,0
        return this.voidVec;
    }

    /** A textual representation of the renderer */
    public String toString() {
        return this.name;
    }
}