package org.sunflow.core.renderer; import org.sunflow.core.Display; import org.sunflow.core.ImageSampler; import org.sunflow.core.IntersectionState; import org.sunflow.core.Options; import org.sunflow.core.Scene; import org.sunflow.core.ShadingState; import org.sunflow.image.Color; import org.sunflow.system.Timer; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; public class SimpleRenderer implements ImageSampler { private Scene scene; private Display display; private int imageWidth, imageHeight; private int numBucketsX, numBucketsY; private int bucketCounter, numBuckets; @Override public boolean prepare(Options options, Scene scene, int w, int h) { this.scene = scene; imageWidth = w; imageHeight = h; numBucketsX = (imageWidth + 31) >>> 5; numBucketsY = (imageHeight + 31) >>> 5; numBuckets = numBucketsX * numBucketsY; return true; } @Override public void render(Display display) { this.display = display; display.imageBegin(imageWidth, imageHeight, 32); // set members variables bucketCounter = 0; // start task Timer timer = new Timer(); timer.start(); BucketThread[] renderThreads = new BucketThread[scene.getThreads()]; for (int i = 0; i < renderThreads.length; i++) { renderThreads[i] = new BucketThread(); renderThreads[i].start(); } for (int i = 0; i < renderThreads.length; i++) { try { renderThreads[i].join(); } catch (InterruptedException e) { UI.printError(Module.BCKT, "Bucket processing thread %d of %d was interrupted", i + 1, renderThreads.length); } finally { renderThreads[i].updateStats(); } } timer.end(); UI.printInfo(Module.BCKT, "Render time: %s", timer.toString()); display.imageEnd(); } private class BucketThread extends Thread { private final IntersectionState istate = new IntersectionState(); @Override public void run() { while (true) { int bx, by; synchronized (SimpleRenderer.this) { if (bucketCounter >= numBuckets) { return; } by = bucketCounter / numBucketsX; bx = bucketCounter % numBucketsX; bucketCounter++; } renderBucket(bx, by, istate); } } void updateStats() { scene.accumulateStats(istate); } } public void renderBucket(int bx, int by, IntersectionState istate) { // pixel sized extents int x0 = bx * 32; int y0 = by * 32; int bw = Math.min(32, imageWidth - x0); int bh = Math.min(32, imageHeight - y0); Color[] bucketRGB = new Color[bw * bh]; float[] bucketAlpha = new float[bw * bh]; for (int y = 0, i = 0; y < bh; y++) { for (int x = 0; x < bw; x++, i++) { ShadingState state = scene.getRadiance(istate, x0 + x, imageHeight - 1 - (y0 + y), 0.0, 0.0, 0.0, 0, 0, null); bucketRGB[i] = (state != null) ? state.getResult() : Color.BLACK; bucketAlpha[i] = (state != null) ? 1 : 0; } } // update pixels display.imageUpdate(x0, y0, bw, bh, bucketRGB, bucketAlpha); } }