All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.llbit.chunky.main.Chunky Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
/* Copyright (c) 2010-2016 Jesper Öqvist 
 *
 * This file is part of Chunky.
 *
 * Chunky is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Chunky 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Chunky.  If not, see .
 */
package se.llbit.chunky.main;

import se.llbit.chunky.PersistentSettings;
import se.llbit.chunky.Plugin;
import se.llbit.chunky.renderer.ConsoleProgressListener;
import se.llbit.chunky.renderer.OutputMode;
import se.llbit.chunky.renderer.RayTracerFactory;
import se.llbit.chunky.renderer.RenderContext;
import se.llbit.chunky.renderer.RenderContextFactory;
import se.llbit.chunky.renderer.RenderController;
import se.llbit.chunky.renderer.RenderManager;
import se.llbit.chunky.renderer.Renderer;
import se.llbit.chunky.renderer.RendererFactory;
import se.llbit.chunky.renderer.SceneProvider;
import se.llbit.chunky.renderer.SnapshotControl;
import se.llbit.chunky.renderer.scene.AsynchronousSceneManager;
import se.llbit.chunky.renderer.scene.PathTracer;
import se.llbit.chunky.renderer.scene.PreviewRayTracer;
import se.llbit.chunky.renderer.scene.Scene;
import se.llbit.chunky.renderer.scene.SceneFactory;
import se.llbit.chunky.renderer.scene.SceneLoadingError;
import se.llbit.chunky.renderer.scene.SceneManager;
import se.llbit.chunky.renderer.scene.SynchronousSceneManager;
import se.llbit.chunky.resources.MinecraftFinder;
import se.llbit.chunky.resources.TexturePackLoader;
import se.llbit.chunky.ui.ChunkyFx;
import se.llbit.chunky.ui.render.RenderControlsTabTransformer;
import se.llbit.json.JsonValue;
import se.llbit.log.Level;
import se.llbit.log.Log;
import se.llbit.log.Receiver;
import se.llbit.util.TaskTracker;
import se.llbit.util.Util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Chunky is a Minecraft mapping and rendering tool created by
 * Jesper Öqvist ([email protected]).
 *
 * 

Read more about Chunky at http://chunky.llbit.se . */ public class Chunky { /** A log receiver suitable for headless rendering. */ private static final Receiver HEADLESS_LOG_RECEIVER = new Receiver() { @Override public void logEvent(Level level, String message) { if (level == Level.ERROR) { System.err.println(); // Clear the current progress line. System.err.println(message); } else { System.out.println(); // Clear the current progress line. System.out.println(message); } } }; public final ChunkyOptions options; private RenderController renderController; private SceneFactory sceneFactory = SceneFactory.DEFAULT; private RenderContextFactory renderContextFactory = RenderContext::new; private RendererFactory rendererFactory = RenderManager::new; private RayTracerFactory previewRayTracerFactory = PreviewRayTracer::new; private RayTracerFactory rayTracerFactory = PathTracer::new; private RenderControlsTabTransformer renderControlsTabTransformer = tabs -> tabs; /** * @return The name of this application, including version string. */ public static String getAppName() { return String.format("Chunky %s", Version.getVersion()); } public Chunky(ChunkyOptions options) { this.options = options; } /** * Start a headless (no GUI) render. * * @return error code */ private int doHeadlessRender() { // TODO: This may not be needed after switching to JavaFX: System.setProperty("java.awt.headless", "true"); Log.setReceiver(HEADLESS_LOG_RECEIVER, Level.INFO, Level.WARNING, Level.ERROR); RenderContext context = renderContextFactory.newRenderContext(this); Renderer renderer = rendererFactory.newRenderer(context, true); SynchronousSceneManager sceneManager = new SynchronousSceneManager(context, renderer); renderer.setSceneProvider(sceneManager); TaskTracker taskTracker = new TaskTracker(new ConsoleProgressListener(), (tracker, previous, name, size) -> new TaskTracker.Task(tracker, previous, name, size) { @Override public void close() { super.close(); long endTime = System.currentTimeMillis(); int seconds = (int) ((endTime - startTime) / 1000); System.out.format("\r%s took %dm %ds%n", name, seconds / 60, seconds % 60); } }); sceneManager.setTaskTracker(taskTracker); renderer.setSnapshotControl(SnapshotControl.DEFAULT); renderer.setOnFrameCompleted((scene, spp) -> { if (SnapshotControl.DEFAULT.saveSnapshot(scene, spp)) { // Save the current frame. scene.saveSnapshot(context.getSceneDirectory(), taskTracker); } if (SnapshotControl.DEFAULT.saveRenderDump(scene, spp)) { // Save the scene description and current render dump. try { sceneManager.saveScene(); } catch (InterruptedException e) { throw new Error(e); } } }); renderer.setRenderTask(taskTracker.backgroundTask()); renderer.setOnRenderCompleted((time, sps) -> { System.out.println("Render job finished."); int seconds = (int) ((time / 1000) % 60); int minutes = (int) ((time / 60000) % 60); int hours = (int) (time / 3600000); System.out.println(String .format("Total rendering time: %d hours, %d minutes, %d seconds", hours, minutes, seconds)); System.out.println("Average samples per second (SPS): " + sps); }); try { sceneManager.loadScene(options.sceneName); if (options.target != -1) { sceneManager.getScene().setTargetSpp(options.target); } sceneManager.getScene().startHeadlessRender(); renderer.start(); renderer.join(); return 0; } catch (FileNotFoundException e) { System.err.format("Scene \"%s\" not found!%n", options.sceneName); return 1; } catch (IOException e) { System.err.format("IO error while loading scene (%s)%n", e.getMessage()); return 1; } catch (SceneLoadingError e) { System.err.format("Scene loading error (%s)%n", e.getMessage()); return 1; } catch (InterruptedException e) { System.err.println("Interrupted while loading scene"); return 1; } finally { renderer.shutdown(); } } /** * Main entry point for Chunky. Chunky should normally be started via * the launcher which sets up the classpath with all dependencies. */ public static void main(final String[] args) { CommandLineOptions cmdline = new CommandLineOptions(args); if (cmdline.configurationError) { System.exit(1); } int exitCode = 0; if (cmdline.mode != CommandLineOptions.Mode.NOTHING) { Chunky chunky = new Chunky(cmdline.options); chunky.loadPlugins(); try { switch (cmdline.mode) { case HEADLESS_RENDER: exitCode = chunky.doHeadlessRender(); break; case SNAPSHOT: exitCode = chunky.doSnapshot(); break; case DEFAULT: ChunkyFx.startChunkyUI(chunky); break; } } catch (Throwable t) { Log.error("Unchecked exception caused Chunky to close.", t); exitCode = 2; } if (exitCode != 0) { System.exit(exitCode); } } } /** * This can be used by plugins to load the default Minecraft textures. * * @throws FileNotFoundException * @throws TexturePackLoader.TextureLoadingError */ public static void loadDefaultTextures() throws FileNotFoundException, TexturePackLoader.TextureLoadingError { TexturePackLoader.loadTexturePack(MinecraftFinder.getMinecraftJarNonNull(), false); } private void loadPlugins() { JsonValue plugins = PersistentSettings.getPlugins(); for (JsonValue plugin : plugins.array().getElementList()) { String jar = plugin.object().get("jar").stringValue(""); String main = plugin.object().get("main").stringValue(""); // The MD5 checksum is only for Jar integrity checking, not security! // Plugin Jar trust is implicit. Only install plugins that you trust! String md5 = plugin.object().get("md5").stringValue(""); boolean enabled = plugin.object().get("enabled").boolValue(true); if (!jar.endsWith(".jar")) { Log.error("Plugin Jar path does not seem to point to a Jar file: " + jar); } if (!enabled) { // Skip disabled plugin. continue; } if (main.isEmpty()) { Log.error("Plugin has no main class declared: " + jar); continue; } if (md5.isEmpty()) { Log.error("Plugin missing MD5 checksum: " + jar); continue; } File pluginJar = new File(jar); if (pluginJar.isFile()) { if (!verifyChecksumMd5(pluginJar, md5)) { Log.error("Plugin is corrupt (MD5 check failed): " + jar); continue; } try { URLClassLoader classLoader = new URLClassLoader(new URL[] {pluginJar.toURI().toURL()}); Class pluginClass = classLoader.loadClass(main); Plugin pluginInstance = (Plugin) pluginClass.newInstance(); pluginInstance.attach(this); Log.info("Plugin loaded: " + jar); } catch (ClassCastException e) { Log.error("Failed to load plugin " + pluginJar.getAbsolutePath() + ". Main plugin class has wrong type", e); } catch (Throwable e) { Log.error("Failed to load plugin " + pluginJar.getAbsolutePath(), e); } } } } private static boolean verifyChecksumMd5(File pluginJar, String expected) { String actual = Util.md5sum(pluginJar); return actual.equalsIgnoreCase(expected); } /** * Save a snapshot for a scene. * *

This currently disregards the various factories for the * render context and scene construction. */ private int doSnapshot() { Log.setReceiver(HEADLESS_LOG_RECEIVER, Level.INFO, Level.WARNING, Level.ERROR); try { File file = options.getSceneDescriptionFile(); Scene scene = new Scene(); try (FileInputStream in = new FileInputStream(file)) { scene.loadDescription(in); // Load description to get current SPP & canvas size. RenderContext context = new RenderContext(this); TaskTracker taskTracker = new TaskTracker(new ConsoleProgressListener(), TaskTracker.Task::new, (tracker, previous, name, size) -> new TaskTracker.Task(tracker, previous, name, size) { @Override public void update() { // Don't report task state to progress listener. } }); scene.initBuffers(); // Initialize the sample buffer. scene.loadDump(context, taskTracker); // Load the render dump. OutputMode outputMode = scene.getOutputMode(); if (options.imageOutputFile.isEmpty()) { String extension = ".png"; if (outputMode == OutputMode.TIFF_32) { extension = ".tiff"; } options.imageOutputFile = String.format("%s-%d%s", scene.name(), scene.spp, extension); } switch (outputMode) { case PNG: System.out.println("Image output mode: PNG"); break; case TIFF_32: System.out.println("Image output mode: TIFF32"); break; } scene.saveFrame(new File(options.imageOutputFile), taskTracker); System.out.println("Saved snapshot to " + options.imageOutputFile); return 0; } } catch (IOException e) { System.err.println("Failed to dump snapshot: " + e.getMessage()); return 1; } } public synchronized SceneManager getSceneManager() { return getRenderController().getSceneManager(); } public boolean sceneInitialized() { return renderController != null; } public RenderController getRenderController() { if (renderController == null) { RenderContext context = renderContextFactory.newRenderContext(this); Renderer renderer = rendererFactory.newRenderer(context, false); AsynchronousSceneManager sceneManager = new AsynchronousSceneManager(context, renderer); SceneProvider sceneProvider = sceneManager.getSceneProvider(); renderer.setSceneProvider(sceneProvider); renderer.start(); sceneManager.start(); renderController = new RenderController(context, renderer, sceneManager, sceneProvider); } return renderController; } public void setRenderContextFactory(RenderContextFactory renderContextFactory) { this.renderContextFactory = renderContextFactory; } public RenderContextFactory getRenderContextFactory() { return renderContextFactory; } public RenderContext getRenderContext() { return getRenderController().getContext(); } public void setSceneFactory(SceneFactory sceneFactory) { this.sceneFactory = sceneFactory; } public SceneFactory getSceneFactory() { return sceneFactory; } public void setPreviewRayTracerFactory(RayTracerFactory previewRayTracerFactory) { this.previewRayTracerFactory = previewRayTracerFactory; } public RayTracerFactory getPreviewRayTracerFactory() { return previewRayTracerFactory; } public void setRayTracerFactory(RayTracerFactory rayTracerFactory) { this.rayTracerFactory = rayTracerFactory; } public RayTracerFactory getRayTracerFactory() { return rayTracerFactory; } public void setRenderControlsTabTransformer( RenderControlsTabTransformer renderControlsTabTransformer) { this.renderControlsTabTransformer = renderControlsTabTransformer; } public RenderControlsTabTransformer getRenderControlsTabTransformer() { return renderControlsTabTransformer; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy