43
43
import org .spongepowered .asm .mixin .injection .callback .CallbackInfo ;
44
44
import org .spongepowered .asm .mixin .injection .callback .CallbackInfoReturnable ;
45
45
46
- import java .util .concurrent .ExecutorService ;
47
- import java .util .concurrent .Executors ;
46
+ import java .util .function .BooleanSupplier ;
48
47
49
48
import javax .annotation .ParametersAreNonnullByDefault ;
50
49
57
56
@ Mixin (ViewFrustum .class )
58
57
public class MixinViewFrustum_RenderHeightFix {
59
58
60
- @ Unique private static final ExecutorService BACKGROUND_EXECUTOR = Executors .newSingleThreadExecutor ((runnable ) -> {
61
- Thread t = new Thread (runnable );
62
- t .setDaemon (true );
63
- t .setName ("ViewFrustum RenderChunk position updater (CubicChunks)" );
64
- return t ;
65
- });
66
-
67
59
@ Shadow @ Final protected World world ;
68
60
@ SuppressWarnings ("MismatchedReadAndWriteOfArray" ) @ Shadow public RenderChunk [] renderChunks ;
69
61
@ Shadow protected int countChunksX ;
70
62
@ Shadow protected int countChunksY ;
71
63
@ Shadow protected int countChunksZ ;
72
64
65
+ @ Unique private int cubicchunks_oldViewX = Integer .MAX_VALUE ; //sufficiently large default value that it can never intersect with real values
66
+ @ Unique private int cubicchunks_oldViewY = Integer .MAX_VALUE ;
67
+ @ Unique private int cubicchunks_oldViewZ = Integer .MAX_VALUE ;
68
+
73
69
@ Inject (method = "updateChunkPositions" , at = @ At (value = "HEAD" ), cancellable = true , require = 1 )
74
70
private void updateChunkPositionsInject (double viewEntityX , double viewEntityZ , CallbackInfo cbi ) {
75
71
if (!((ICubicWorld ) world ).isCubicWorld ()) {
@@ -85,44 +81,148 @@ private void updateChunkPositionsInject(double viewEntityX, double viewEntityZ,
85
81
int dz = countChunksZ ;
86
82
RenderChunk [] chunks = this .renderChunks ;
87
83
88
- BACKGROUND_EXECUTOR .submit (() -> {
89
- int minX = viewX - (dx >> 1 );
90
- int minY = viewY - (dy >> 1 );
91
- int minZ = viewZ - (dz >> 1 );
92
- int px = MathHelper .intFloorDiv (minX , dx ) * dx ;
93
- int py = MathHelper .intFloorDiv (minY , dy ) * dy ;
94
- int pz = MathHelper .intFloorDiv (minZ , dz ) * dz ;
95
-
96
- for (int zIndex = 0 ; zIndex < this .countChunksZ ; zIndex ++) {
97
- int blockZ = pz + zIndex ;
98
- if (blockZ < minZ ) {
99
- blockZ += dz ;
84
+ //the coordinate of the RenderChunk in the lowest corner
85
+ int minX = viewX - (dx >> 1 );
86
+ int minY = viewY - (dy >> 1 );
87
+ int minZ = viewZ - (dz >> 1 );
88
+
89
+ //the coordinate of a RenderChunk which sits at the origin. Wraps around within the min/max range
90
+ int px = MathHelper .intFloorDiv (minX , dx ) * dx ;
91
+ int py = MathHelper .intFloorDiv (minY , dy ) * dy ;
92
+ int pz = MathHelper .intFloorDiv (minZ , dz ) * dz ;
93
+
94
+ //use longs here just in case the int values overflow (they shouldn't ever, but i want to play it safe)
95
+ long changeX = (long ) viewX - this .cubicchunks_oldViewX ;
96
+ long changeY = (long ) viewY - this .cubicchunks_oldViewY ;
97
+ long changeZ = (long ) viewZ - this .cubicchunks_oldViewZ ;
98
+ this .cubicchunks_oldViewX = viewX ;
99
+ this .cubicchunks_oldViewY = viewY ;
100
+ this .cubicchunks_oldViewZ = viewZ ;
101
+
102
+ if (Math .abs (changeX ) <= 1 && Math .abs (changeY ) <= 1 && Math .abs (changeZ ) <= 1 ) {
103
+ //fast-path: the camera has moved by at most one cube so we only need to perform updates along a 2d plane
104
+
105
+ /*
106
+ * d: 4
107
+ *
108
+ * 0123456789 0123456789 0123456789 0123456789 0123456789 .
109
+ * min: # min: # min: # min: # min: # .
110
+ * p: # p: # p: # p: # p: # .
111
+ * 0+p: # 0+p: * # 0+p: * # 0+p: * # 0+p: # .
112
+ * 1+p: # 1+p: # 1+p: * # 1+p: * # 1+p: # .
113
+ * 2+p: # 2+p: # 2+p: # 2+p: * # 2+p: # .
114
+ * 3+p: # 3+p: # 3+p: # 3+p: # 3+p: # .
115
+ */
116
+
117
+ if (changeX != 0 ) { //we'll need to update one layer of RenderChunks perpendicular to the YZ plane
118
+ int xIndex = Math .floorMod (changeX < 0 ? minX - px : minX - px - 1 , dx );
119
+ int blockX = cubicchunks_getBlockCoord (xIndex , dx , px , minX );
120
+
121
+ for (int zIndex = 0 ; zIndex < dz ; zIndex ++) {
122
+ int blockZ = cubicchunks_getBlockCoord (zIndex , dz , pz , minZ );
123
+ int idxZ = zIndex * dy * dx ;
124
+
125
+ for (int yIndex = 0 ; yIndex < dy ; yIndex ++) {
126
+ int blockY = cubicchunks_getBlockCoord (yIndex , dy , py , minY );
127
+ int idxYZ = idxZ + yIndex * dx ;
128
+
129
+ chunks [idxYZ + xIndex ].setPosition (blockX , blockY , blockZ );
130
+ }
131
+ }
132
+ }
133
+
134
+ if (changeY != 0 ) { //we'll need to update one layer of RenderChunks perpendicular to the XZ plane
135
+ int yIndex = Math .floorMod (changeY < 0 ? minY - py : minY - py - 1 , dy );
136
+ int blockY = cubicchunks_getBlockCoord (yIndex , dy , py , minY );
137
+
138
+ for (int zIndex = 0 ; zIndex < dz ; zIndex ++) {
139
+ int blockZ = cubicchunks_getBlockCoord (zIndex , dz , pz , minZ );
140
+ int idxZ = zIndex * dy * dx ;
141
+
142
+ int idxYZ = idxZ + yIndex * dx ;
143
+
144
+ for (int xIndex = 0 ; xIndex < dx ; xIndex ++) {
145
+ int blockX = cubicchunks_getBlockCoord (xIndex , dx , px , minX );
146
+
147
+ chunks [idxYZ + xIndex ].setPosition (blockX , blockY , blockZ );
148
+ }
100
149
}
101
- blockZ <<= 4 ;
102
- int idxZ = zIndex * this .countChunksY * this .countChunksX ;
150
+ }
103
151
104
- for (int yIndex = 0 ; yIndex < this .countChunksY ; yIndex ++) {
105
- int blockY = py + yIndex ;
106
- if (blockY < minY ) {
107
- blockY += dy ;
152
+ if (changeZ != 0 ) { //we'll need to update one layer of RenderChunks perpendicular to the XY plane
153
+ int zIndex = Math .floorMod (changeZ < 0 ? minZ - pz : minZ - pz - 1 , dz );
154
+ int blockZ = cubicchunks_getBlockCoord (zIndex , dz , pz , minZ );
155
+ int idxZ = zIndex * dy * dx ;
156
+
157
+ for (int yIndex = 0 ; yIndex < dy ; yIndex ++) {
158
+ int blockY = cubicchunks_getBlockCoord (yIndex , dy , py , minY );
159
+ int idxYZ = idxZ + yIndex * dx ;
160
+
161
+ for (int xIndex = 0 ; xIndex < dx ; xIndex ++) {
162
+ int blockX = cubicchunks_getBlockCoord (xIndex , dx , px , minX );
163
+
164
+ chunks [idxYZ + xIndex ].setPosition (blockX , blockY , blockZ );
108
165
}
109
- blockY <<= 4 ;
110
- int idxYZ = idxZ + yIndex * this .countChunksX ;
111
- for (int xIndex = 0 ; xIndex < this .countChunksX ; xIndex ++) {
112
- int blockX = px + xIndex ;
113
- if (blockX < minX ) {
114
- blockX += dx ;
166
+ }
167
+ }
168
+
169
+ //run the original loop to double-check that all RenderChunks are in the correct position
170
+ // (doing this cancels out any benefits from skipping unchanged RenderChunks, but only runs with assertions enabled)
171
+ assert ((BooleanSupplier ) () -> {
172
+ for (int zIndex = 0 ; zIndex < dz ; zIndex ++) {
173
+ int blockZ = cubicchunks_getBlockCoord (zIndex , dz , pz , minZ );
174
+ int idxZ = zIndex * dy * dx ;
175
+
176
+ for (int yIndex = 0 ; yIndex < dy ; yIndex ++) {
177
+ int blockY = cubicchunks_getBlockCoord (yIndex , dy , py , minY );
178
+ int idxYZ = idxZ + yIndex * dx ;
179
+
180
+ for (int xIndex = 0 ; xIndex < dx ; xIndex ++) {
181
+ int blockX = cubicchunks_getBlockCoord (xIndex , dx , px , minX );
182
+ BlockPos pos = chunks [idxYZ + xIndex ].getPosition ();
183
+
184
+ if (pos .getX () != blockX || pos .getY () != blockY || pos .getZ () != blockZ ) {
185
+ return false ;
186
+ }
115
187
}
116
- blockX <<= 4 ;
117
- RenderChunk renderer = chunks [idxYZ + xIndex ];
118
- renderer .setPosition (blockX , blockY , blockZ );
188
+ }
189
+ }
190
+ return true ;
191
+ }).getAsBoolean () : "Not all RenderChunks are in the correct position!" ;
192
+ } else {
193
+ //slow path, this behaves like the original vanilla code.
194
+ //loop over all RenderChunks and set their position.
195
+
196
+ //original loop, cleaned up:
197
+ for (int zIndex = 0 ; zIndex < dz ; zIndex ++) {
198
+ int blockZ = cubicchunks_getBlockCoord (zIndex , dz , pz , minZ );
199
+ int idxZ = zIndex * dy * dx ;
200
+
201
+ for (int yIndex = 0 ; yIndex < dy ; yIndex ++) {
202
+ int blockY = cubicchunks_getBlockCoord (yIndex , dy , py , minY );
203
+ int idxYZ = idxZ + yIndex * dx ;
204
+
205
+ for (int xIndex = 0 ; xIndex < dx ; xIndex ++) {
206
+ int blockX = cubicchunks_getBlockCoord (xIndex , dx , px , minX );
207
+
208
+ chunks [idxYZ + xIndex ].setPosition (blockX , blockY , blockZ );
119
209
}
120
210
}
121
211
}
122
- });
212
+ }
213
+
123
214
cbi .cancel ();
124
215
}
125
216
217
+ @ Unique
218
+ private static int cubicchunks_getBlockCoord (int index , int d , int p , int min ) {
219
+ int coord = p + index ;
220
+ if (coord < min ) {
221
+ coord += d ;
222
+ }
223
+ return coord << 4 ;
224
+ }
225
+
126
226
@ Inject (method = "getRenderChunk" , at = @ At (value = "HEAD" ), cancellable = true , require = 1 )
127
227
private void getRenderChunkInject (BlockPos pos , CallbackInfoReturnable <RenderChunk > cbi ) {
128
228
if (!((ICubicWorld ) world ).isCubicWorld ()) {
0 commit comments