/*
* This file is part of Cubic Chunks Mod, licensed under the MIT License (MIT).
*
* Copyright (c) 2015 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package cubicchunks.asm.mixin.core.client;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.ViewFrustum;
import net.minecraft.client.renderer.chunk.RenderChunk;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.entity.Entity;
import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.chunk.Chunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Group;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
import java.util.List;
import cubicchunks.util.Coords;
import cubicchunks.world.ICubicWorld;
import cubicchunks.world.column.Column;
import static cubicchunks.asm.JvmNames.BLOCK_POS_GETY;
import static cubicchunks.asm.JvmNames.CHUNK_GET_ENTITY_LISTS;
import static cubicchunks.asm.JvmNames.WORLD_CLIENT_GET_CHUNK_FROM_BLOCK_COORDS;
/**
* Fixes renderEntities crashing when rendering cubes
* that are not at existing array index in chunk.getEntityLists(),
* <p>
* Allows to render cubes outside of 0..256 height range.
*/
@Mixin(RenderGlobal.class)
public class MixinRenderGlobal {
private BlockPos position;
@Shadow private int renderDistanceChunks;
@Shadow private ViewFrustum viewFrustum;
/**
* This allows to get the Y position of rendered entity by injecting itself directly before call to
* chunk.getEntityLists
*/
@Group(name = "renderEntitiesFix", max = 3)
@Inject(method = "renderEntities",
at = @At(value = "INVOKE", target = WORLD_CLIENT_GET_CHUNK_FROM_BLOCK_COORDS),
locals = LocalCapture.CAPTURE_FAILHARD,
require = 1)
public void onGetPosition(Entity renderViewEntity, ICamera camera, float partialTicks, CallbackInfo ci,
int pass, double d0, double d1, double d2,
Entity entity, double d3, double d4, double d5,
List<Entity> list, List<Entity> list1, List<Entity> list2,
BlockPos.PooledMutableBlockPos pos, Iterator<RenderGlobal.ContainerLocalRenderInformation> var21,
RenderGlobal.ContainerLocalRenderInformation info) {
ICubicWorld world = (ICubicWorld) info.renderChunk.getWorld();
if (world.isCubicWorld()) {
this.position = info.renderChunk.getPosition();
} else {
this.position = null;
}
}
/**
* After chunk.getEntityLists() renderGlobal needs to get correct element of the array.
* The array element number is calculated using renderChunk.getPosition().getY() / 16.
* getY() is redirected to this method to always return 0.
* <p>
* Then chunk.getEntityLists is redirected to a method that returns a 1-element array.
*/
@Group(name = "renderEntitiesFix")
@Redirect(method = "renderEntities", at = @At(value = "INVOKE", target = BLOCK_POS_GETY), require = 1)
public int getRenderChunkYPos(BlockPos pos) {
//position is null when it's not cubic chunks renderer
if (this.position != null) {
return 0;//must be 0 (or anything between 0 and 15)
}
return pos.getY();
}
/**
* Return a 1-element array for Cubic Chunks world,
* or original chunk.getEntityLists if not cubic chunks world.
*/
@SuppressWarnings("unchecked")
@Group(name = "renderEntitiesFix")
@Redirect(method = "renderEntities", at = @At(value = "INVOKE", target = CHUNK_GET_ENTITY_LISTS), require = 1)
public ClassInheritanceMultiMap<Entity>[] getEntityList(Chunk chunk) {
if (position == null) {
return chunk.getEntityLists(); //TODO: is this right?
}
return new ClassInheritanceMultiMap[]{
((Column) chunk)
.getCube(Coords.blockToCube(position.getY()))
.getEntityContainer().getEntitySet()
};
}
/**
* Overwrite getRenderChunk(For)Offset to support extended height.
*
* @author Barteks2x
* @reason Remove hardcoded height checks, it's a simple method and doing it differently would be problematic and
* confusing (Inject with local capture into BlockPos.getX() and redirect of BlockPos.getY())
*/
@Overwrite
private RenderChunk getRenderChunkOffset(BlockPos playerPos, RenderChunk renderChunkBase, EnumFacing facing) {
BlockPos blockpos = renderChunkBase.getBlockPosOffset16(facing);
return MathHelper.abs(playerPos.getX() - blockpos.getX()) > this.renderDistanceChunks*16 ? null :
MathHelper.abs(playerPos.getY() - blockpos.getY()) > this.renderDistanceChunks*16 ? null :
MathHelper.abs(playerPos.getZ() - blockpos.getZ()) > this.renderDistanceChunks*16 ? null :
this.viewFrustum.getRenderChunk(blockpos);
}
}