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

se.llbit.chunky.renderer.scene.SceneDescription Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
/* Copyright (c) 2013-2015 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.renderer.scene;

import org.jastadd.util.PrettyPrinter;
import se.llbit.chunky.PersistentSettings;
import se.llbit.chunky.renderer.OutputMode;
import se.llbit.chunky.renderer.Postprocess;
import se.llbit.chunky.renderer.Refreshable;
import se.llbit.chunky.renderer.RenderMode;
import se.llbit.chunky.renderer.ResetReason;
import se.llbit.chunky.world.ChunkPosition;
import se.llbit.json.JsonArray;
import se.llbit.json.JsonMember;
import se.llbit.json.JsonObject;
import se.llbit.json.JsonParser;
import se.llbit.json.JsonParser.SyntaxError;
import se.llbit.json.JsonValue;
import se.llbit.math.Vector3;
import se.llbit.util.JSONifiable;
import se.llbit.util.ZipExport;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Basic scene description.
 *
 * @author Jesper Öqvist 
 */
public class SceneDescription implements Refreshable, JSONifiable {
  public static final String EXTENSION = ".json";

  /**
   * The current Scene Description Format (SDF) version.
   */
  public static final int SDF_VERSION = 7;

  public int sdfVersion = -1;
  public String name = "default";

  /**
   * Canvas width.
   */
  public int width;

  /**
   * Canvas height.
   */
  public int height;
  protected double exposure = Scene.DEFAULT_EXPOSURE;
  public Postprocess postprocess = Postprocess.DEFAULT;
  public OutputMode outputMode = OutputMode.DEFAULT;
  public long renderTime;

  /**
   * Current SPP for the scene.
   */
  public int spp = 0;

  /**
   * Target SPP for the scene.
   */
  protected int sppTarget = PersistentSettings.getSppTargetDefault();

  /**
   * Recursive ray depth limit (not including Russian Roulette).
   */
  protected int rayDepth = PersistentSettings.getRayDepthDefault();

  protected final Sky sky = new Sky(this);
  protected final Camera camera = new Camera(this);
  protected final Sun sun = new Sun(this);

  protected String worldPath = "";
  protected int worldDimension = 0;

  protected RenderMode mode = RenderMode.PREVIEW;

  protected int dumpFrequency = Scene.DEFAULT_DUMP_FREQUENCY;
  protected boolean saveSnapshots = false;

  protected boolean emittersEnabled = Scene.DEFAULT_EMITTERS_ENABLED;
  protected double emitterIntensity = Scene.DEFAULT_EMITTER_INTENSITY;
  protected boolean sunEnabled = true;

  /**
   * Water opacity modifier.
   */
  protected double waterOpacity = PersistentSettings.getWaterOpacity();
  protected double waterVisibility = PersistentSettings.getWaterVisibility();
  protected int waterHeight = PersistentSettings.getWaterHeight();
  protected boolean stillWater = PersistentSettings.getStillWater();
  protected boolean useCustomWaterColor = PersistentSettings.getUseCustomWaterColor();
  protected final Vector3 waterColor =
      new Vector3(PersistentSettings.getWaterColorRed(), PersistentSettings.getWaterColorGreen(),
          PersistentSettings.getWaterColorBlue());
  protected final Vector3 fogColor =
      new Vector3(PersistentSettings.getFogColorRed(), PersistentSettings.getFogColorGreen(),
          PersistentSettings.getFogColorBlue());

  /**
   * Enables fast fog algorithm
   */
  protected boolean fastFog = true;

  /**
   * Fog thickness.
   */
  protected double fogDensity;

  protected boolean biomeColors = true;
  protected boolean transparentSky = false;

  protected boolean renderActors = true;

  protected Collection chunks = new ArrayList<>();

  protected JsonObject cameraPresets = new JsonObject();
  /**
   * Indicates if the render should be forced to reset.
   */
  protected ResetReason resetReason = ResetReason.NONE;

  /**
   * Parse the scene description from a JSON file.
   *
   * @param in input stream - will be closed
   */
  public void loadDescription(InputStream in) throws IOException {
    try (JsonParser parser = new JsonParser(in)) {
      JsonObject desc = parser.parse().object();
      fromJson(desc);
    } catch (SyntaxError e) {
      throw new IOException("JSON syntax error");
    }
  }

  /**
   * Load the scene description from a JSON file.
   *
   * @param out output stream - will be closed
   */
  public void saveDescription(OutputStream out) throws IOException {
    PrettyPrinter pp = new PrettyPrinter("  ", new PrintStream(out));
    toJson().prettyPrint(pp);
    out.close();
  }

  @Override public synchronized JsonObject toJson() {
    JsonObject desc = new JsonObject();
    desc.add("sdfVersion", SDF_VERSION);
    desc.add("name", name);
    desc.add("width", width);
    desc.add("height", height);
    desc.add("exposure", exposure);
    desc.add("postprocess", postprocess.name());
    desc.add("outputMode", outputMode.name());
    desc.add("renderTime", renderTime);
    desc.add("spp", spp);
    desc.add("sppTarget", sppTarget);
    desc.add("rayDepth", rayDepth);
    desc.add("pathTrace", mode != RenderMode.PREVIEW);
    desc.add("dumpFrequency", dumpFrequency);
    desc.add("saveSnapshots", saveSnapshots);

    desc.add("emittersEnabled", emittersEnabled);
    desc.add("emitterIntensity", emitterIntensity);
    desc.add("sunEnabled", sunEnabled);
    desc.add("stillWater", stillWater);
    desc.add("waterOpacity", waterOpacity);
    desc.add("waterVisibility", waterVisibility);
    desc.add("useCustomWaterColor", useCustomWaterColor);
    if (useCustomWaterColor) {
      JsonObject colorObj = new JsonObject();
      colorObj.add("red", waterColor.x);
      colorObj.add("green", waterColor.y);
      colorObj.add("blue", waterColor.z);
      desc.add("waterColor", colorObj);
    }
    JsonObject fogColorObj = new JsonObject();
    fogColorObj.add("red", fogColor.x);
    fogColorObj.add("green", fogColor.y);
    fogColorObj.add("blue", fogColor.z);
    desc.add("fogColor", fogColorObj);
    desc.add("fastFog", fastFog);
    desc.add("biomeColorsEnabled", biomeColors);
    desc.add("transparentSky", transparentSky);
    desc.add("fogDensity", fogDensity);
    desc.add("waterHeight", waterHeight);
    desc.add("renderActors", renderActors);

    // save world info
    if (!worldPath.isEmpty()) {
      JsonObject world = new JsonObject();
      world.add("path", worldPath);
      world.add("dimension", worldDimension);
      desc.add("world", world);
    }

    desc.add("camera", camera.toJson());
    desc.add("sun", sun.toJson());
    desc.add("sky", sky.toJson());

    desc.add("cameraPresets", cameraPresets.fullCopy());

    JsonArray chunkList = new JsonArray();
    for (ChunkPosition pos : chunks) {
      JsonArray chunk = new JsonArray();
      chunk.add(pos.x);
      chunk.add(pos.z);
      chunkList.add(chunk);
    }
    desc.add("chunkList", chunkList);

    return desc;
  }

  @Override public synchronized void fromJson(JsonObject desc) {
    setResetReason(ResetReason.SCENE_LOADED);

    sdfVersion = desc.get("sdfVersion").intValue(-1);
    name = desc.get("name").stringValue("unknown");
    width = desc.get("width").intValue(Scene.MIN_CANVAS_WIDTH);
    height = desc.get("height").intValue(Scene.MIN_CANVAS_HEIGHT);
    exposure = desc.get("exposure").doubleValue(Scene.DEFAULT_EXPOSURE);
    postprocess = Postprocess.get(desc.get("postprocess").stringValue(""));
    outputMode = OutputMode.get(desc.get("outputMode").stringValue(""));
    sppTarget = desc.get("sppTarget").intValue(PersistentSettings.getSppTargetDefault());
    rayDepth = desc.get("rayDepth").intValue(PersistentSettings.getRayDepthDefault());
    boolean pathTrace = desc.get("pathTrace").boolValue(false);
    if (pathTrace) {
      mode = RenderMode.PAUSED;
    } else {
      mode = RenderMode.PREVIEW;
    }
    dumpFrequency = desc.get("dumpFrequency").intValue(Scene.DEFAULT_DUMP_FREQUENCY);
    saveSnapshots = desc.get("saveSnapshots").boolValue(false);

    emittersEnabled = desc.get("emittersEnabled").boolValue(Scene.DEFAULT_EMITTERS_ENABLED);
    emitterIntensity = desc.get("emitterIntensity").doubleValue(Scene.DEFAULT_EMITTER_INTENSITY);
    sunEnabled = desc.get("sunEnabled").boolValue(true);
    stillWater = desc.get("stillWater").boolValue(false);
    waterOpacity = desc.get("waterOpacity").doubleValue(PersistentSettings.getWaterOpacity());
    waterVisibility =
        desc.get("waterVisibility").doubleValue(PersistentSettings.getWaterVisibility());
    useCustomWaterColor =
        desc.get("useCustomWaterColor").boolValue(PersistentSettings.getUseCustomWaterColor());
    if (useCustomWaterColor) {
      JsonObject colorObj = desc.get("waterColor").object();
      waterColor.x = colorObj.get("red").doubleValue(PersistentSettings.getWaterColorRed());
      waterColor.y = colorObj.get("green").doubleValue(PersistentSettings.getWaterColorGreen());
      waterColor.z = colorObj.get("blue").doubleValue(PersistentSettings.getWaterColorBlue());
    }
    JsonObject fogColorObj = desc.get("fogColor").object();
    fogColor.x = fogColorObj.get("red").doubleValue(PersistentSettings.getFogColorRed());
    fogColor.y = fogColorObj.get("green").doubleValue(PersistentSettings.getFogColorGreen());
    fogColor.z = fogColorObj.get("blue").doubleValue(PersistentSettings.getFogColorBlue());
    fastFog = desc.get("fastFog").boolValue(true);
    biomeColors = desc.get("biomeColorsEnabled").boolValue(true);
    transparentSky = desc.get("transparentSky").boolValue(false);
    fogDensity = desc.get("fogDensity").doubleValue(Scene.DEFAULT_FOG_DENSITY);
    waterHeight = desc.get("waterHeight").intValue(0);
    renderActors = desc.get("renderActors").boolValue(true);

    // load world info
    JsonObject world = desc.get("world").object();
    worldPath = world.get("path").stringValue("");
    worldDimension = world.get("dimension").intValue(0);

    camera.fromJson(desc.get("camera").object());
    sun.fromJson(desc.get("sun").object());
    sky.fromJson(desc.get("sky").object());

    cameraPresets = desc.get("cameraPresets").object();

    // Read these after loading camera, sun, sky because they refresh the scene.
    spp = desc.get("spp").intValue(0);
    renderTime = desc.get("renderTime").longValue(0);

    chunks.clear();
    JsonArray chunkList = desc.get("chunkList").array();
    for (JsonValue elem : chunkList.getElementList()) {
      JsonArray chunk = elem.array();
      int x = chunk.get(0).intValue(Integer.MAX_VALUE);
      int z = chunk.get(1).intValue(Integer.MAX_VALUE);
      if (x != Integer.MAX_VALUE && z != Integer.MAX_VALUE) {
        chunks.add(ChunkPosition.get(x, z));
      }
    }
  }

  /**
   * Called when the scene description has been altered in a way that
   * forces the rendering to restart.
   */
  @Override public synchronized void refresh() {
    if (mode == RenderMode.PAUSED) {
      mode = RenderMode.RENDERING;
    }
    spp = 0;
    renderTime = 0;
    setResetReason(ResetReason.SETTINGS_CHANGED);
    notifyAll();
  }

  /**
   * @return The sun state object.
   */
  public Sun sun() {
    return sun;
  }

  /**
   * @return The sky state object.
   */
  public Sky sky() {
    return sky;
  }

  /**
   * @return The camera state object.
   */
  public Camera camera() {
    return camera;
  }

  /**
   * Delete all scene files from the scene directory, leaving only
   * snapshots untouched.
   */
  public void delete(File sceneDir) {
    String[] extensions =
        {".json", ".dump", ".octree", ".foliage", ".grass", ".json.backup", ".dump.backup",};
    for (String extension : extensions) {
      File file = new File(sceneDir, name + extension);
      if (file.isFile()) {
        //noinspection ResultOfMethodCallIgnored
        file.delete();
      }
    }
  }

  /**
   * Export the scene to a zip file.
   */
  public void exportToZip(File targetFile) {
    String[] extensions = {".json", ".dump", ".octree", ".foliage", ".grass",};
    ZipExport.zip(targetFile, PersistentSettings.getSceneDirectory(), name, extensions);
  }

  public void saveCameraPreset(String name) {
    camera.name = name;
    for (JsonMember member : cameraPresets.getMemberList()) {
      if (member.getName().equals(name)) {
        member.setValue(camera.toJson());
        return;
      }
    }
    cameraPresets.add(name, camera.toJson());
  }

  public void loadCameraPreset(String name) {
    JsonValue value = cameraPresets.get(name);
    if (!value.isUnknown()) {
      camera.fromJson(value.object());
      refresh();
    }
  }

  public void deleteCameraPreset(String name) {
    for (int i = 0; i < cameraPresets.getNumMember(); ++i) {
      if (cameraPresets.getMember(i).getName().equals(name)) {
        cameraPresets.getMemberList().removeChild(i);
        return;
      }
    }
  }

  public JsonObject getCameraPresets() {
    return cameraPresets;
  }

  public RenderMode getMode() {
    return mode;
  }

  public void setFogDensity(double newValue) {
    if (newValue != fogDensity) {
      this.fogDensity = newValue;
      refresh();
    }
  }

  public double getFogDensity() {
    return fogDensity;
  }

  public void setFastFog(boolean value) {
    if (fastFog != value) {
      fastFog = value;
      refresh();
    }
  }

  public boolean fastFog() {
    return fastFog;
  }

  /**
   * @return {@code true} if volumetric fog is enabled
   */
  public boolean fogEnabled() {
    return fogDensity > 0.0;
  }

  public OutputMode getOutputMode() {
    return outputMode;
  }

  public void setOutputMode(OutputMode mode) {
    outputMode = mode;
  }

  public int numberOfChunks() {
    return chunks.size();
  }

  /**
   * Clears the reset reason and returns the previous reason.
   * @return the current reset reason
   */
  public synchronized ResetReason getResetReason() {
    return resetReason;
  }

  public void setResetReason(ResetReason resetReason) {
    if (this.resetReason != ResetReason.SCENE_LOADED) {
      this.resetReason = resetReason;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy