-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add hitboxes inside some voxelshapes of cuboids (e.g. extended p…
…iston base) fixes: Lithium removes hitboxes inside some blocks. #60 squashed: fix small mistakes and improve bad comments
- Loading branch information
Showing
4 changed files
with
183 additions
and
10 deletions.
There are no files selected for viewing
130 changes: 130 additions & 0 deletions
130
...va/me/jellysquid/mods/lithium/common/shapes/VoxelShapeCuboidWithCollisionBoxesInside.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package me.jellysquid.mods.lithium.common.shapes; | ||
|
||
import net.minecraft.util.math.AxisCycleDirection; | ||
import net.minecraft.util.math.Box; | ||
import net.minecraft.util.shape.VoxelSet; | ||
import net.minecraft.util.shape.VoxelShape; | ||
|
||
/** | ||
* An efficient implementation of {@link VoxelShape} for a shape with one simple cuboid. | ||
* This is an alternative to VoxelShapeSimpleCube with extra hitboxes inside. | ||
* Vanilla has extra hitboxes at steps of 1/8th or 1/4th of a block depending on the exact coordinates of the shape. | ||
* We are mimicking the effect on collisions here, as otherwise some contraptions would not behave like vanilla. | ||
* @author 2No2Name | ||
*/ | ||
public class VoxelShapeCuboidWithCollisionBoxesInside extends VoxelShapeSimpleCube { | ||
//In bit aligned shapes the bitset adds segments are between minX/Y/Z and maxX/Y/Z. | ||
//Segments all have the same size. There is an additional collision box between two adjacent segments (if both are inside the shape) | ||
private final int xSegments; | ||
private final int ySegments; | ||
private final int zSegments; | ||
|
||
public VoxelShapeCuboidWithCollisionBoxesInside(VoxelSet voxels, double minX, double minY, double minZ, double maxX, double maxY, double maxZ, int xRes, int yRes, int zRes) { | ||
super(voxels, minX, minY, minZ, maxX, maxY, maxZ); | ||
//If the VoxelShape doesn't contain any extra collision boxes in vanilla on the given axis (only one segment in total) | ||
//We set the segment count to 1 to signal that there are no inside shape segment borders, which is the fast branch in calculatePenetration | ||
this.xSegments = xRes <= 1 ? 1 : (1 << xRes); | ||
this.ySegments = yRes <= 1 ? 1 : (1 << yRes); | ||
this.zSegments = zRes <= 1 ? 1 : (1 << zRes); | ||
} | ||
|
||
/** | ||
* Constructor for use in offset() calls. | ||
*/ | ||
public VoxelShapeCuboidWithCollisionBoxesInside(VoxelSet voxels, int xSegments, int ySegments, int zSegments, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { | ||
super(voxels, minX, minY, minZ, maxX, maxY, maxZ); | ||
|
||
this.xSegments = xSegments; | ||
this.ySegments = ySegments; | ||
this.zSegments = zSegments; | ||
} | ||
|
||
@Override | ||
public VoxelShape offset(double x, double y, double z) { | ||
return new VoxelShapeCuboidWithCollisionBoxesInside(this.voxels, this.xSegments, this.ySegments, this.zSegments, this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z); | ||
} | ||
|
||
@Override | ||
double calculatePenetration(AxisCycleDirection dir, Box box, double maxDist) { | ||
switch (dir) { | ||
case NONE: | ||
return this.calculatePenetration(this.minX, this.maxX, this.xSegments, box.minX, box.maxX, maxDist); | ||
case FORWARD: | ||
return this.calculatePenetration(this.minZ, this.maxZ, this.zSegments, box.minZ, box.maxZ, maxDist); | ||
case BACKWARD: | ||
return this.calculatePenetration(this.minY, this.maxY, this.ySegments, box.minY, box.maxY, maxDist); | ||
default: | ||
throw new IllegalArgumentException(); | ||
} | ||
} | ||
|
||
/** | ||
* Determine how far the movement is possible. | ||
*/ | ||
private double calculatePenetration(double aMin, double aMax, final int segmentCount, double bMin, double bMax, double maxDist) { | ||
double gap; | ||
|
||
if (maxDist > 0.0D) { | ||
gap = aMin - bMax; | ||
|
||
if (gap < -EPSILON) { | ||
//already far enough inside this shape to not collide with the surface | ||
if (segmentCount == 1) { | ||
//no extra segments to collide with, because only one segment in total | ||
return maxDist; | ||
} | ||
//extra hitboxes inside this shape, evenly spaced out by segmentSize | ||
double distanceToEnd = aMax - bMax; | ||
int segmentsToEnd = (int)(distanceToEnd * segmentCount); | ||
if (segmentsToEnd < 0) | ||
return maxDist; | ||
//using 1D because the BitSetVoxelSet we are mimicking is always assuming a size of 1 for the whole voxel | ||
double distanceToSegment = distanceToEnd - ((segmentsToEnd + 1D) / segmentCount); | ||
//apply a possible backwards movement because vanilla has it (vanilla: very slightly behind hitbox -> move back) | ||
if (distanceToSegment > -EPSILON) { | ||
segmentsToEnd++; | ||
} | ||
if (segmentsToEnd > 0) //no need to check segmentsToEnd >= segmentCount, because gap < -EPSILON already | ||
//limit movement to the next segment-segment hitbox | ||
maxDist = Math.min(maxDist, distanceToEnd - segmentsToEnd / (double) segmentCount); | ||
return maxDist; | ||
} else if (maxDist < gap) { | ||
//outside the shape and still far enough away for no collision at all | ||
return maxDist; | ||
|
||
//not really inside the shape and collision is happening | ||
} else if (gap < 0.0D) { //also gap >= -EPSILON here | ||
//already slightly inside the shape but within the margin | ||
//vanilla (VoxelShape.calculateMaxDistance) stops movement in this case and instead tries to adjust position to be outside the shape again | ||
//This is usually cancelled during the next call of calculatePenetration due to abs(maxDist) < EPSILON returning 0. | ||
//If this is the last iteration it might be used without being set to 0. | ||
return -gap; | ||
} | ||
//allow moving up to the shape but not into it | ||
return gap; | ||
} else { | ||
//whole code again, just negated for the other direction | ||
gap = aMax - bMin; | ||
|
||
if (gap > EPSILON) { | ||
if (segmentCount == 1) { | ||
return maxDist; | ||
} | ||
double negDistanceToEnd = aMin - bMin; | ||
int negSegmentsToEnd = (int)(negDistanceToEnd * segmentCount); | ||
double negDistanceToSegment = negDistanceToEnd - (negSegmentsToEnd - 1D) / segmentCount; | ||
if (negDistanceToSegment < EPSILON) { | ||
negSegmentsToEnd--; | ||
} | ||
if (negSegmentsToEnd < 0) | ||
maxDist = Math.max(maxDist, negDistanceToEnd - negSegmentsToEnd / (double) segmentCount); | ||
return maxDist; | ||
} else if (maxDist > gap) { | ||
return maxDist; | ||
} else if (gap > 0.0D) { | ||
return -gap; | ||
} | ||
return gap; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters