Package jray.ray

Source Code of jray.ray.Renderer$ExtJComponent

package jray.ray;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.swing.JComponent;

import jray.common.Ray;
import jray.common.Vect3;
import jray.math.Vect;
import jray.math.intersections.LongColors;
import jray.ray.tracer.BackwardRayTracer;
import jray.ray.tracer.BackwardRayTracerHQ;


/**
* Renders a Scene to an image as seen by a specified Camera.
* <p>
* To utilize the full power of multiple CPU-cores, this class maintains a
* thread pool and splits up the image into many horizontal stripes. The total count of
* image-parts is a multiple of the thread count (default is <code>threadCount * 4</code>).
*/
public class Renderer {

    /**
     * Count of image stripes
     */
    private final int splitCount;
    /**
     * Count of threads in the thread pool
     */
    private final int threadCount;
    /**
     * The scene the renderer is looking at.
     */
    private Scene scene;
    /**
     * RayTraceLogic objects assigned to its thread. This map is exclusively accessed by the
     * method getLogic() which returns the RayTraceLogic accordingly to the thread calling it.
     * This method additionally creates RayTraceLogic objects and stores them in this field
     * if a thread uses it the first time.
     */
    private Map<Thread, BackwardRayTracer> logics;
    /**
     * An ExecutorService wrapping a thread pool, which consists of <code>threadCount</code> threads.
     */
    private ExecutorService service;
    /**
     * An array containing a task for each part of the image. Each task knows the id of it's image part and
     * is reused for each redraw of the image.
     */
    private Callable<?>[] tasks;
    /**
     * An array containing the futures of the tasks - they are not used to retrieve return values but to
     * wait for finished tasks.
     */
    private Future<?>[] futures;
    /**
     * The width of the image in pixel
     */
    private int widthPx;
    /**
     * The height of the image in pixel
     */
    private int heightPx;
    /**
     * The content of the image with one integer per pixel
     */
    private int[] ibuf;
    private long[] lbuf;
    /**
     * Factor used to scale the brightness of the resulting image
     */
    private double brigthnessScale = 1.0;
    /**
     * The framerate - used to measure performance
     */
    private double frameRate = 0;

    /**
     * Creates a new renderer.
     *
     * @param scene the scene
     * @param threadCount size of the thread pool
     */
    public Renderer(Scene scene, int threadCount) {
        this.threadCount = threadCount;
        this.splitCount = threadCount * 4;

        System.out.println("Initializing Renderer...");
        System.out.println(" - Object Count:            " + scene.getObjects().size());
        System.out.println(" - Thread Count:            " + threadCount);
        System.out.println(" - Image Parts:             " + splitCount);

        System.out.print(" - ThreadPool:              ");
        service = Executors.newFixedThreadPool(this.threadCount);
        System.out.println("Launched");

        System.out.print(" - Callables and Futures:   ");
        tasks = new Callable<?>[splitCount];
        futures = new Future<?>[splitCount];

        int splits = splitCount;
        for (int i = 0; i < splits; i++) {
            final int id = i;

            tasks[i] = new Callable<Object>() {

                @Override
                public Object call() throws Exception {
                    renderImagePart(id);
                    return null;
                }
            };
        }
        System.out.println("Ready");
        setScene(scene);

        System.out.println("Renderer initialised successfully - READY FOR RUN");
    }

    /**
     * Initializes the Renderer using a scene and a camera
     *
     * @param scene the scene
     */
    public synchronized void setScene(Scene scene) {
        System.out.println("Initializing Scene: " + scene.getName());
        this.scene = scene;
        logics = Collections.synchronizedMap(new HashMap<Thread, BackwardRayTracer>(threadCount));
    }

    /**
     * Gets the RayTraceLogic for the current thread.
     * @return the RayTraceLogic for the current thread.
     */
    private BackwardRayTracer getLogic() {
        Thread me = Thread.currentThread();
        BackwardRayTracer ret = logics.get(me);

        if (ret == null) {//create one if it does not already exist
            ret = new BackwardRayTracerHQ(scene);
            logics.put(me, ret);
        }

        return ret;
    }

    /**
     * Renders the scene to an image
     *
     * @param image the image where data is stored
     */
    private synchronized void renderImage(BufferedImage image) {
        if (Renderer.this.service.isShutdown()) {
            throw new RuntimeException("This renderer must no longer be used as it has been shut down!");
        }

        try {
            if (lbuf == null || widthPx != image.getWidth() || heightPx != image.getHeight()) {
                lbuf = new long[(image.getWidth()) * (image.getHeight())];
            }

            widthPx = image.getWidth();
            heightPx = image.getHeight();

            ibuf = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

            for (int i = 0; i < futures.length; i++) {
                futures[i] = service.submit(tasks[i]);
            }

            for (Future<?> f : futures) {
                f.get();
            }

            // search max color value in whole picture
            int max = 255;
            for (int i = 0; i < lbuf.length; i++) {
                int m = LongColors.getMax(lbuf[i]);
                if (m > max) {
                    max = m;
                }
            }
            double scale = 255.0 / max * brigthnessScale;

            for(int i = 0, j = 0;j < heightPx; i++) {
                long color = lbuf[(j) * (widthPx) + i];

                ibuf[j * widthPx + i] = LongColors.toInt(LongColors.scale(color, scale));

                if(i == widthPx - 1) {
                    ++j;
                    i = -1;
                }
            }

        } catch (Exception e) {
            System.out.println("Exception in renderImage:");
            e.printStackTrace(System.out);
            System.exit(1);
        }
    }

    /**
     * Renders the part <code>id</code> of <code>splitCount</code> of the image to ibuf.
     *
     * @param id a number from <code>0</code> to <code>splitCount-1</code>
     */
    private void renderImagePart(int id) {
        int slotHeight = heightPx / splitCount;
        int from = slotHeight * id;
        int to = slotHeight * (id + 1);
        if (id == splitCount - 1) {
            to = heightPx;
        }

        BackwardRayTracer logic = getLogic();
        Vect3 rayDirection = new Vect3();
        Camera camera = scene.getCamera();

        Ray ray = new Ray(new Vect3(camera.getPosition()), rayDirection);

        Vect3 vertAdd = new Vect3(camera.getViewPaneHeightVector());
        Vect.scale(vertAdd, 1.0 / (heightPx - 1), vertAdd);
        Vect3 horzAdd = new Vect3(camera.getViewPaneWidthVector());
        Vect.scale(horzAdd, 1.0 / (widthPx - 1), horzAdd);

        for (int i = from; i < to; i++) {
            for (int j = 0; j < widthPx; j++) {
             
              Vect.subtract(camera.getViewPaneEdge(), ray.getOrigin(), rayDirection);
              Vect.addMultiple(rayDirection, vertAdd, i, rayDirection);
              Vect.addMultiple(rayDirection, horzAdd, j, rayDirection);
               
                rayDirection.normalize();

                long color = logic.shoot(ray);

                lbuf[i * widthPx + j] = color;
            }
        }
    }

    /**
     * Shuts down this Renderer. The thread pool will be terminated and the components generated by this instance
     * will throw exceptions upon invocation.
     */
    public synchronized void shutdown() {
        try {
            System.out.println("Shutting down Renderer...");
            service.shutdown();
            System.out.println("Waiting for ThreadPool Shutdown...");
            service.awaitTermination(1, TimeUnit.DAYS);
            System.out.println("Renderer shutdown complete!");
        } catch (InterruptedException t) {
        }
    }

    /**
     * Creates and returns a component which can be added to a frame. This component will be updated using the image the renderer sees.
     *
     * @return the "window" to the scene
     */
    public JComponent getDrawComponent() {
        final ExtJComponent ret = new ExtJComponent();

        /**
         * Checks if the component has been resized and updates the size of the BufferdImage accordingly
         */
        ret.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                Component c = e.getComponent();
                ret.updateImageDimensions();
                scene.getCamera().setScreenDimensions(c.getWidth(), c.getHeight());
            }
        });

        return ret;
    }

    private final class ExtJComponent extends JComponent {

        private static final long serialVersionUID = 1L;
        BufferedImage image = null;
        private int widthPx = -1, heightPx = -1;

        private ExtJComponent() {
            updateImageDimensions();
        }

        @Override
        public synchronized void paintComponent(Graphics g) {
            super.paintComponent(g);

            long startNanos = System.nanoTime();
            // Render image and update Component
            Renderer.this.renderImage(image);
            g.drawImage(image, 0, 0, null);
            frameRate = 1000000000.0 / (System.nanoTime() - startNanos);
        }

        public synchronized void updateImageDimensions() {
            widthPx = this.getWidth();
            heightPx = this.getHeight();
            image = new BufferedImage((widthPx > 0 ? widthPx : 1), (heightPx > 0 ? heightPx : 1), BufferedImage.TYPE_INT_ARGB);
        }
    }

    public double getBrigthnessScale() {
        return brigthnessScale;
    }

    public void setBrigthnessScale(double brigthnessScale) {
        this.brigthnessScale = brigthnessScale;
    }

    public double getFrameRate() {
        return frameRate;
    }
}
TOP

Related Classes of jray.ray.Renderer$ExtJComponent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.