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

se.llbit.chunky.world.ChunkView 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.world;

import se.llbit.chunky.ui.MapViewMode;
import se.llbit.math.QuickMath;

/**
 * Abstract representation of a view over a map of chunks.
 *
 * @author Jesper Öqvist 
 */
public class ChunkView {
  /**
   * Minimum block scale for the map view.
   */
  public static final int BLOCK_SCALE_MIN = 1;

  /**
   * Maximum block scale for the map view.
   */
  public static final int BLOCK_SCALE_MAX = 32 * 16;

  /**
   * Default block scale for the map view.
   */
  public static final int DEFAULT_BLOCK_SCALE = 4 * 16;

  /**
   * A zero-size chunk view useful as a default chunk view for an uninitialized map.
   */
  public static final ChunkView EMPTY = new ChunkView(0, 0, 0, 0, DEFAULT_BLOCK_SCALE,
      MapViewMode.AUTO, World.SEA_LEVEL) {
    @Override public boolean isChunkVisible(int x, int z) {
      return false;
    }

    @Override public boolean isRegionVisible(int x, int z) {
      return false;
    }
  };

  public final MapViewMode renderer;

  public final int layer;

  // Center position.
  public final double x;
  public final double z;

  // Visible chunks.
  public final double x0;
  public final double z0;
  public final double x1;
  public final double z1;

  // Rendered chunks.
  public final int cx0;
  public final int cz0;
  public final int cx1;
  public final int cz1;

  // Preloaded chunks.
  public final int px0;
  public final int pz0;
  public final int px1;
  public final int pz1;

  // Rendered regions.
  public final int prx0;
  public final int prz0;
  public final int prx1;
  public final int prz1;

  public final int width;
  public final int height;
  public final int scale;
  public final int chunkScale;


  public ChunkView(double x, double z, int width, int height, MapViewMode renderer, int layer) {
    this(x, z, width, height, 16, renderer, layer);
  }

  public ChunkView(ChunkView other) {
    this(other.x, other.z, other.width, other.height, other.scale, other.renderer, other.layer);
  }

  public ChunkView(double x, double z, int width, int height, int scale, MapViewMode renderer,
      int layer) {
    this.renderer = renderer;
    this.layer = Math.max(0, Math.min(Chunk.Y_MAX - 1, layer));
    scale = clampScale(scale);
    this.scale = scale;
    if (this.scale <= 12) {
      chunkScale = 1;
    } else if (this.scale <= 12 * 16) {
      chunkScale = 16;
    } else {
      this.chunkScale = 16 * 16;
    }
    this.x = x;
    this.z = z;
    double cw = width / (2. * this.scale);
    double ch = height / (2. * this.scale);
    this.x0 = x - cw;
    this.x1 = x + cw;
    this.z0 = z - ch;
    this.z1 = z + ch;
    this.width = width;
    this.height = height;
    // Visible chunks [integer coordinates]:
    cx0 = (int) QuickMath.floor(x0);
    cx1 = (int) QuickMath.floor(x1);
    cz0 = (int) QuickMath.floor(z0);
    cz1 = (int) QuickMath.floor(z1);
    if (this.scale >= 16) {
      px0 = cx0 - 1;
      px1 = cx1 + 1;
      pz0 = cz0 - 1;
      pz1 = cz1 + 1;
      prx0 = px0 >> 5;
      prx1 = px1 >> 5;
      prz0 = pz0 >> 5;
      prz1 = pz1 >> 5;
    } else {
      // Visible regions.
      int irx0 = cx0 >> 5;
      int irx1 = cx1 >> 5;
      int irz0 = cz0 >> 5;
      int irz1 = cz1 >> 5;
      prx0 = irx0;
      prx1 = irx1;
      prz0 = irz0;
      prz1 = irz1;
      px0 = prx0 << 5;
      px1 = (prx1 << 5) + 31;
      pz0 = prz0 << 5;
      pz1 = (prz1 << 5) + 31;
    }
  }

  public boolean shouldPreload(Chunk chunk) {
    if (chunk.isEmpty()) {
      return false;
    }
    ChunkPosition pos = chunk.getPosition();
    return isChunkVisible(pos.x, pos.z);
  }

  @Override public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (obj instanceof ChunkView) {
      ChunkView other = (ChunkView) obj;
      return scale == other.scale
          && px0 == other.px0
          && px1 == other.px1
          && pz0 == other.pz0
          && pz1 == other.pz1
          && layer == other.layer
          && renderer == other.renderer;
    }
    return false;
  }

  public boolean isVisible(ChunkPosition pos) {
    return shouldPreload(pos);
  }


  /**
   * Determines if a chunk or region is visible based on the view scale.
   * If the scale is greater than or equal to 16 then the test is for chunk visibility,
   * otherwise region visibility is checked.
   */
  public boolean shouldPreload(ChunkPosition pos) {
    return chunkScale >= 16 ? isChunkVisible(pos.x, pos.z) : isRegionVisible(pos.x, pos.z);
  }

  public boolean isChunkVisible(ChunkPosition chunk) {
    return isChunkVisible(chunk.x, chunk.z);
  }

  public boolean isChunkVisible(int x, int z) {
    return px0 <= x && px1 >= x && pz0 <= z && pz1 >= z;
  }

  public boolean isRegionVisible(ChunkPosition pos) {
    return isRegionVisible(pos.x, pos.z);
  }

  public boolean isRegionVisible(int x, int z) {
    return prx0 <= x && prx1 >= x && prz0 <= z && prz1 >= z;
  }

  @Override public String toString() {
    return String.format("[(%d, %d), (%d, %d)]", px0, pz0, px1, pz1);
  }

  /**
   * Clamp the block scale to the minimum / maximum values if it is outside
   * the valid value range.
   * @return clamped scale value
   */
  public static int clampScale(int scale) {
    return Math.max(BLOCK_SCALE_MIN, Math.min(BLOCK_SCALE_MAX, scale));
  }

  /**
   * @param other the previous view state
   * @return {@code true} if changing to this view from the given old
   * view should trigger a map repaint.
   */
  public boolean shouldRepaint(ChunkView other) {
    if (px0 != other.px0
        || px1 != other.px1
        || pz0 != other.pz0
        || pz1 != other.pz1
        || renderer != other.renderer) {
      return true;
    }
    if (renderer == MapViewMode.LAYER && layer != other.layer) {
      return true;
    }
    return scale != other.scale;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy