Skip to content

Commit 2901a5f

Browse files
committed
backported shape caching
1 parent 8f6e813 commit 2901a5f

File tree

6 files changed

+111
-64
lines changed

6 files changed

+111
-64
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ java {
137137
}
138138
}
139139

140-
val changelogFile = file("temp/changelog.txt")
140+
val changelogFile = file("build/changelog.txt")
141141

142142
curseforge {
143143
val curseApiKey = optionalProperty("curseApiKey") ?: System.getenv("CURSEFORGE_API_KEY")

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
modName=DynamicTrees
22
modId=dynamictrees
3-
modVersion=1.4.7
3+
modVersion=1.4.8
44

55
group=com.ferreusveritas.dynamictrees
66

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.ferreusveritas.dynamictrees.api.treedata;
2+
3+
public record BranchShapeState(byte down, byte up, byte north, byte south, byte west, byte east, byte core){
4+
5+
private static final int[] BLOCK_START = new int[8]; // start offsets for each family of shapes (by core)
6+
public static final int TOTAL_STATES;
7+
static {
8+
int sum = 0;
9+
for (int core = 1; core < 8; ++core) {
10+
int size = (int) Math.pow(core + 1, 6);
11+
BLOCK_START[core] = sum;
12+
sum += size;
13+
}
14+
TOTAL_STATES = sum; // 446,963
15+
}
16+
17+
/**
18+
* This optimized indexing takes into account that each side radius
19+
* is capped by the core radius (side <= core).
20+
* That way the total size is reduced considerably.
21+
* @return the index where the voxel shape should be stored
22+
*/
23+
public int toIndex() {
24+
final int base = core + 1;
25+
// compute mixed-radix local index for the six sides, down is most-significant, east is the least.
26+
// local = (((((down * base + up) * base + north) * base + south) * base + west) * base + east)
27+
int local = down;
28+
local = local * base + up;
29+
local = local * base + north;
30+
local = local * base + south;
31+
local = local * base + west;
32+
local = local * base + east;
33+
return BLOCK_START[core] + local;
34+
}
35+
36+
public static BranchShapeState fromArray(byte[] radii){
37+
return new BranchShapeState(radii[0], radii[1], radii[2], radii[3], radii[4], radii[5], radii[6]);
38+
}
39+
40+
}

src/main/java/com/ferreusveritas/dynamictrees/block/branch/BasicBranchBlock.java

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -388,41 +388,11 @@ public boolean isLadder(BlockState state, LevelReader level, BlockPos pos, Livin
388388
(!state.hasProperty(WATERLOGGED) || !state.getValue(WATERLOGGED));
389389
}
390390

391-
@Nonnull
392-
@Override
393-
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
394-
int thisRadiusInt = getRadius(state);
395-
double radius = thisRadiusInt / 16.0;
396-
VoxelShape core = Shapes.box(0.5 - radius, 0.5 - radius, 0.5 - radius, 0.5 + radius, 0.5 + radius, 0.5 + radius);
397-
398-
for (Direction dir : Direction.values()) {
399-
int sideRadiusInt = Math.min(getSideConnectionRadius(level, pos, thisRadiusInt, dir), thisRadiusInt);
400-
double sideRadius = sideRadiusInt / 16.0f;
401-
if (sideRadius > 0.0f) {
402-
double gap = 0.5f - sideRadius;
403-
AABB aabb = new AABB(0.5 - sideRadius, 0.5 - sideRadius, 0.5 - sideRadius, 0.5 + sideRadius, 0.5 + sideRadius, 0.5 + sideRadius);
404-
aabb = aabb.expandTowards(dir.getStepX() * gap, dir.getStepY() * gap, dir.getStepZ() * gap);
405-
core = Shapes.or(core, Shapes.create(aabb));
406-
}
407-
}
408-
409-
return core;
410-
}
411-
412391
@Override
413392
public int getRadiusForConnection(BlockState state, BlockGetter level, BlockPos pos, BranchBlock from, Direction side, int fromRadius) {
414393
return getRadius(state);
415394
}
416395

417-
protected int getSideConnectionRadius(BlockGetter level, BlockPos pos, int radius, Direction side) {
418-
final BlockPos deltaPos = pos.relative(side);
419-
final BlockState blockState = CoordUtils.getStateSafe(level, deltaPos);
420-
421-
// If adjacent block is not loaded assume there is no connection.
422-
return blockState == null ? 0 : TreeHelper.getTreePart(blockState).getRadiusForConnection(blockState, level, deltaPos, this, side, radius);
423-
}
424-
425-
426396
///////////////////////////////////////////
427397
// NODE ANALYSIS
428398
///////////////////////////////////////////

src/main/java/com/ferreusveritas/dynamictrees/block/branch/BasicRootsBlock.java

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,14 @@ public boolean checkForRot(LevelAccessor level, BlockPos pos, Species species, i
504504
// SHAPE
505505
//////////////////////////////
506506

507+
@Override
508+
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
509+
if (isFullBlock(state)) {
510+
return Shapes.block();
511+
}
512+
return super.getShape(state, level, pos, context);
513+
}
514+
507515
@Override
508516
public VoxelShape getCollisionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
509517
if (isFullBlock(pState)) {
@@ -515,38 +523,6 @@ public VoxelShape getCollisionShape(BlockState pState, BlockGetter pLevel, Block
515523
return super.getCollisionShape(pState, pLevel, pPos, pContext);
516524
}
517525

518-
@Nonnull
519-
@Override
520-
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext pContext) {
521-
if (isFullBlock(state)) {
522-
return Shapes.block();
523-
}
524-
int thisRadiusInt = getRadius(state);
525-
double radius = thisRadiusInt / 16.0;
526-
VoxelShape core = Shapes.box(0.5 - radius, 0.5 - radius, 0.5 - radius, 0.5 + radius, 0.5 + radius, 0.5 + radius);
527-
528-
for (Direction dir : Direction.values()) {
529-
int sideRadiusInt = Math.min(getSideConnectionRadius(level, pos, thisRadiusInt, dir), thisRadiusInt);
530-
double sideRadius = sideRadiusInt / 16.0f;
531-
if (sideRadius > 0.0f) {
532-
double gap = 0.5f - sideRadius;
533-
AABB aabb = new AABB(0.5 - sideRadius, 0.5 - sideRadius, 0.5 - sideRadius, 0.5 + sideRadius, 0.5 + sideRadius, 0.5 + sideRadius);
534-
aabb = aabb.expandTowards(dir.getStepX() * gap, dir.getStepY() * gap, dir.getStepZ() * gap);
535-
core = Shapes.or(core, Shapes.create(aabb));
536-
}
537-
}
538-
539-
return core;
540-
}
541-
542-
protected int getSideConnectionRadius(BlockGetter level, BlockPos pos, int radius, Direction side) {
543-
final BlockPos deltaPos = pos.relative(side);
544-
final BlockState blockState = CoordUtils.getStateSafe(level, deltaPos);
545-
546-
// If adjacent block is not loaded assume there is no connection.
547-
return blockState == null ? 0 : TreeHelper.getTreePart(blockState).getRadiusForConnection(blockState, level, deltaPos, this, side, radius);
548-
}
549-
550526
@Override
551527
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
552528
if (isTransparent(pState)){

src/main/java/com/ferreusveritas/dynamictrees/block/branch/BranchBlock.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.ferreusveritas.dynamictrees.api.FutureBreakable;
55
import com.ferreusveritas.dynamictrees.api.TreeHelper;
66
import com.ferreusveritas.dynamictrees.api.network.MapSignal;
7+
import com.ferreusveritas.dynamictrees.api.treedata.BranchShapeState;
78
import com.ferreusveritas.dynamictrees.api.treedata.TreePart;
89
import com.ferreusveritas.dynamictrees.block.BlockWithDynamicHardness;
910
import com.ferreusveritas.dynamictrees.block.leaves.DynamicLeavesBlock;
@@ -47,9 +48,13 @@
4748
import net.minecraft.world.level.pathfinder.PathComputationType;
4849
import net.minecraft.world.level.storage.loot.LootDataManager;
4950
import net.minecraft.world.level.storage.loot.LootTable;
51+
import net.minecraft.world.phys.AABB;
5052
import net.minecraft.world.phys.BlockHitResult;
5153
import net.minecraft.world.phys.HitResult;
5254
import net.minecraft.world.phys.Vec3;
55+
import net.minecraft.world.phys.shapes.CollisionContext;
56+
import net.minecraft.world.phys.shapes.Shapes;
57+
import net.minecraft.world.phys.shapes.VoxelShape;
5358
import net.minecraftforge.common.ForgeMod;
5459
import net.minecraftforge.common.ToolActions;
5560

@@ -64,6 +69,9 @@ public abstract class BranchBlock extends BlockWithDynamicHardness implements Tr
6469
public static final int MAX_RADIUS = 8;
6570
public static final String NAME_SUFFIX = "_branch";
6671

72+
//This cache does not include radius == 8, it's just Shapes.block().
73+
protected static final VoxelShape[] shapeCache = new VoxelShape[BranchShapeState.TOTAL_STATES];
74+
6775
public static DynamicTrees.DestroyMode destroyMode = DynamicTrees.DestroyMode.SLOPPY;
6876

6977
/**
@@ -298,6 +306,59 @@ public RenderShape getRenderShape(BlockState state) {
298306
return RenderShape.MODEL;
299307
}
300308

309+
310+
@Override
311+
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
312+
byte[] radii = new byte[7];
313+
int radius = getRadius(state);
314+
315+
//skip all the fancy shape stuff if the trunk is just a block.
316+
if (radius == 8) return Shapes.block();
317+
318+
radii[6] = (byte)radius; //last radius is the core
319+
320+
for (Direction dir : Direction.values()) {
321+
radii[dir.ordinal()] = (byte)Math.min(getSideConnectionRadius(level, pos, radius, dir), radius);
322+
}
323+
int shapeStateIndex = BranchShapeState.fromArray(radii).toIndex();
324+
325+
VoxelShape cachedShape = shapeCache[shapeStateIndex];
326+
if (cachedShape != null){
327+
return cachedShape;
328+
}
329+
330+
VoxelShape newShape = generateNewShape(radii);
331+
shapeCache[shapeStateIndex] = newShape;
332+
return newShape;
333+
}
334+
335+
private static VoxelShape generateNewShape(byte[] radii) {
336+
double radius = radii[6] / 16.0;
337+
VoxelShape shape = Shapes.create(makeCube(radius));
338+
for (Direction dir : Direction.values()) {
339+
double sideRadius = radii[dir.ordinal()] / 16.0f;
340+
if (sideRadius > 0.0f) {
341+
double gap = 0.5f - sideRadius;
342+
AABB aabb = makeCube(sideRadius);
343+
aabb = aabb.expandTowards(dir.getStepX() * gap, dir.getStepY() * gap, dir.getStepZ() * gap);
344+
shape = Shapes.or(shape, Shapes.create(aabb));
345+
}
346+
}
347+
return shape;
348+
}
349+
350+
protected static AABB makeCube(double radius) {
351+
return new AABB(0.5 - radius, 0.5 - radius, 0.5 - radius, 0.5 + radius, 0.5 + radius, 0.5 + radius);
352+
}
353+
354+
protected int getSideConnectionRadius(BlockGetter level, BlockPos pos, int radius, Direction side) {
355+
final BlockPos deltaPos = pos.relative(side);
356+
final BlockState blockState = CoordUtils.getStateSafe(level, deltaPos);
357+
358+
// If adjacent block is not loaded assume there is no connection.
359+
return blockState == null ? 0 : TreeHelper.getTreePart(blockState).getRadiusForConnection(blockState, level, deltaPos, this, side, radius);
360+
}
361+
301362
///////////////////////////////////////////
302363
// GROWTH
303364
///////////////////////////////////////////

0 commit comments

Comments
 (0)