/*
* Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.logic.world;
import java.util.logging.Logger;
import org.spout.api.geo.cuboid.Chunk;
import org.spout.api.material.BlockMaterial;
import org.spout.api.material.MaterialRegistry;
import org.terasology.math.Region3i;
import org.terasology.math.TeraMath;
import org.terasology.math.Vector3i;
import org.terasology.teraspout.TeraBlock;
import org.terasology.teraspout.TeraChunk;
import org.terasology.teraspout.TeraSpout;
/**
* @author Immortius
*/
public class WorldView {
private static Logger logger = Logger.getLogger(WorldView.class.getName());
private Vector3i offset;
private Region3i chunkRegion;
private Region3i blockRegion;
private TeraChunk[] chunks;
private Vector3i chunkPower;
private Vector3i chunkSize;
private Vector3i chunkFilterSize;
public static WorldView createLocalView(Vector3i pos, ChunkProvider chunkProvider) {
Region3i region = Region3i.createFromCenterExtents(pos, new Vector3i(1, 0, 1));
return createWorldView(region, Vector3i.one(), chunkProvider);
}
public static WorldView createSubview(Vector3i pos, int extent, ChunkProvider chunkProvider) {
Region3i region = TeraMath.getChunkRegionAroundBlockPos(pos, extent);
return createWorldView(region, new Vector3i(-region.min().x, 0, -region.min().z), chunkProvider);
}
public static WorldView createWorldView(Region3i region, Vector3i offset, ChunkProvider chunkProvider) {
TeraChunk[] chunks = new TeraChunk[region.size().x * region.size().z];
for (Vector3i chunkPos : region) {
TeraChunk chunk = chunkProvider.getChunk(chunkPos);
if (chunk == null) {
return null;
}
int index = (chunkPos.x - region.min().x) + region.size().x * (chunkPos.z - region.min().z);
chunks[index] = chunk;
}
return new WorldView(chunks, region, offset);
}
public WorldView(TeraChunk[] chunks, Region3i chunkRegion, Vector3i offset) {
this.chunkRegion = chunkRegion;
this.chunks = chunks;
this.offset = offset;
setChunkSize(new Vector3i(Chunk.BLOCKS.SIZE, Chunk.BLOCKS.SIZE, Chunk.BLOCKS.SIZE));
}
public Region3i getChunkRegion() {
return chunkRegion;
}
public TeraBlock getBlock(float x, float y, float z) {
return getBlock(TeraMath.floorToInt(x + 0.5f), TeraMath.floorToInt(y + 0.5f), TeraMath.floorToInt(z + 0.5f));
}
public TeraBlock getBlock(Vector3i pos) {
return getBlock(pos.x, pos.y, pos.z);
}
// TODO: Review
public TeraBlock getBlock(int blockX, int blockY, int blockZ) {
if (!blockRegion.encompasses(blockX, blockY, blockZ)) {
return TeraSpout.getInstance().getBlock((BlockMaterial) MaterialRegistry.get(0));
}
int chunkIndex = relChunkIndex(blockX, blockY, blockZ);
return chunks[chunkIndex].getBlock(TeraMath.calcBlockPos(blockX, blockY, blockZ, chunkFilterSize));
}
public byte getSunlight(float x, float y, float z) {
return getSunlight(TeraMath.floorToInt(x + 0.5f), TeraMath.floorToInt(y + 0.5f), TeraMath.floorToInt(z + 0.5f));
}
public byte getSunlight(Vector3i pos) {
return getSunlight(pos.x, pos.y, pos.z);
}
public byte getLight(float x, float y, float z) {
return getLight(TeraMath.floorToInt(x + 0.5f), TeraMath.floorToInt(y + 0.5f), TeraMath.floorToInt(z + 0.5f));
}
public byte getLight(Vector3i pos) {
return getLight(pos.x, pos.y, pos.z);
}
public byte getSunlight(int blockX, int blockY, int blockZ) {
if (!blockRegion.encompasses(blockX, blockY, blockZ)) {
return 0;
}
int chunkIndex = relChunkIndex(blockX, blockY, blockZ);
return chunks[chunkIndex].getSunlight(TeraMath.calcBlockPos(blockX, blockY, blockZ, chunkFilterSize));
}
public byte getLight(int blockX, int blockY, int blockZ) {
if (!blockRegion.encompasses(blockX, blockY, blockZ)) {
return 0;
}
int chunkIndex = relChunkIndex(blockX, blockY, blockZ);
return chunks[chunkIndex].getLight(TeraMath.calcBlockPos(blockX, blockY, blockZ, chunkFilterSize));
}
public void setLight(Vector3i pos, byte light) {
setLight(pos.x, pos.y, pos.z, light);
}
public void setSunlight(Vector3i pos, byte light) {
setSunlight(pos.x, pos.y, pos.z, light);
}
public void setSunlight(int blockX, int blockY, int blockZ, byte light) {
if (blockRegion.encompasses(blockX, blockY, blockZ)) {
int chunkIndex = relChunkIndex(blockX, blockY, blockZ);
chunks[chunkIndex].setSunlight(TeraMath.calcBlockPos(blockX, blockY, blockZ, chunkFilterSize), light);
}
}
public void setLight(int blockX, int blockY, int blockZ, byte light) {
if (blockRegion.encompasses(blockX, blockY, blockZ)) {
int chunkIndex = relChunkIndex(blockX, blockY, blockZ);
chunks[chunkIndex].setLight(TeraMath.calcBlockPos(blockX, blockY, blockZ, chunkFilterSize), light);
}
}
public void setDirtyAround(Vector3i blockPos) {
for (Vector3i pos : TeraMath.getChunkRegionAroundBlockPos(blockPos, 1)) {
chunks[pos.x + offset.x + chunkRegion.size().x * (pos.z + offset.z)].setDirty(true);
}
}
public void setDirtyAround(Region3i blockRegion) {
Vector3i minPos = new Vector3i(blockRegion.min());
minPos.sub(1, 0, 1);
Vector3i maxPos = new Vector3i(blockRegion.max());
maxPos.add(1, 0, 1);
Vector3i minChunk = TeraMath.calcChunkPos(minPos, chunkPower);
Vector3i maxChunk = TeraMath.calcChunkPos(maxPos, chunkPower);
for (Vector3i pos : Region3i.createFromMinMax(minChunk, maxChunk)) {
chunks[pos.x + offset.x + chunkRegion.size().x * (pos.z + offset.z)].setDirty(true);
}
}
public void lock() {
for (TeraChunk chunk : chunks) {
chunk.lock();
}
}
public void unlock() {
for (TeraChunk chunk : chunks) {
chunk.unlock();
}
}
public boolean isValidView() {
for (TeraChunk chunk : chunks) {
if (chunk.isDisposed()) {
return false;
}
}
return true;
}
int relChunkIndex(int x, int y, int z) {
return TeraMath.calcChunkPosX(x, chunkPower.x) + offset.x + chunkRegion.size().x * (TeraMath.calcChunkPosZ(z, chunkPower.z) + offset.z);
}
public void setChunkSize(Vector3i chunkSize) {
this.chunkSize = chunkSize;
this.chunkFilterSize = new Vector3i(TeraMath.ceilPowerOfTwo(chunkSize.x) - 1, 0, TeraMath.ceilPowerOfTwo(chunkSize.z) - 1);
this.chunkPower = new Vector3i(TeraMath.sizeOfPower(chunkSize.x), 0, TeraMath.sizeOfPower(chunkSize.z));
Vector3i blockMin = new Vector3i();
blockMin.sub(offset);
blockMin.mult(chunkSize.x, 0, chunkSize.z);
Vector3i blockSize = chunkRegion.size();
blockSize.mult(chunkSize.x, chunkSize.y, chunkSize.z);
this.blockRegion = Region3i.createFromMinAndSize(blockMin, blockSize);
}
}