@@ -42,20 +42,41 @@ void ChunkConnectivityGraph::Update(MemoryArena& trans_arena, World& world, cons
42
42
s32 chunk_y;
43
43
s32 chunk_z;
44
44
u16 from;
45
- u16 dirs ;
45
+ u16 traversed ;
46
46
};
47
47
48
+ // Record the visit state for each chunk, so they are only visited once from each direction
49
+ struct VisitState {
50
+ u8 directions;
51
+
52
+ inline bool CanVisit (BlockFace through_face) const {
53
+ return !(directions & (1 << (u8 )through_face));
54
+ }
55
+
56
+ inline void VisitThrough (BlockFace through_face) {
57
+ directions |= (1 << (u8 )through_face);
58
+ }
59
+ };
60
+
61
+ constexpr size_t kVisitStateCount = kChunkCacheSize * kChunkCacheSize * kChunkColumnCount ;
62
+
63
+ VisitState* visit_states = memory_arena_push_type_count (&trans_arena, VisitState, kVisitStateCount );
64
+ memset (visit_states, 0 , sizeof (VisitState) * kVisitStateCount );
65
+
48
66
ProcessChunk* process_queue = memory_arena_push_type (&trans_arena, ProcessChunk);
49
67
size_t process_queue_size = 0 ;
50
68
51
- process_queue[process_queue_size++] = {start_chunk->chunk_x , start_chunk->chunk_y , start_chunk->chunk_z ,
52
- ( u16 )BlockFace::Down, ( u16 ) 0 };
69
+ process_queue[process_queue_size++] = {start_chunk->chunk_x , start_chunk->chunk_y , start_chunk->chunk_z , ( u16 ) 0xFF ,
70
+ 0 };
53
71
54
72
Frustum frustum = camera.GetViewFrustum ();
55
73
56
- size_t process_index = 0 ;
57
- while (process_index < process_queue_size) {
58
- ProcessChunk process_chunk = process_queue[process_index];
74
+ size_t largest_queue_size = 1 ;
75
+
76
+ while (process_queue_size > 0 ) {
77
+ // Pop front of queue and replace it with the last item so it doesn't grow forever.
78
+ ProcessChunk process_chunk = process_queue[0 ];
79
+ process_queue[0 ] = process_queue[--process_queue_size];
59
80
60
81
size_t x_index = world::GetChunkCacheIndex (process_chunk.chunk_x );
61
82
size_t z_index = world::GetChunkCacheIndex (process_chunk.chunk_z );
@@ -64,7 +85,11 @@ void ChunkConnectivityGraph::Update(MemoryArena& trans_arena, World& world, cons
64
85
ChunkSectionInfo& info = world.chunk_infos [z_index][x_index];
65
86
66
87
if (info.bitmask & (1 << process_chunk.chunk_y )) {
67
- if (info.dirty_connectivity_set & (1 << process_chunk.chunk_y )) {
88
+ // We are a non-empty chunk, so check if we are dirty.
89
+ bool is_dirty = info.dirty_connectivity_set & (1 << process_chunk.chunk_y );
90
+ bool is_loaded = world.chunks [z_index][x_index].chunks [process_chunk.chunk_y ];
91
+
92
+ if (is_dirty && is_loaded) {
68
93
connect_set.Build (world, *world.chunks [z_index][x_index].chunks [process_chunk.chunk_y ]);
69
94
info.dirty_connectivity_set &= ~(1 << process_chunk.chunk_y );
70
95
}
@@ -75,6 +100,11 @@ void ChunkConnectivityGraph::Update(MemoryArena& trans_arena, World& world, cons
75
100
for (size_t i = 0 ; i < 6 ; ++i) {
76
101
BlockFace through_face = (BlockFace)i;
77
102
ChunkOffset offset = kOffsets [i];
103
+ BlockFace opposite_face = GetOppositeFace (through_face);
104
+
105
+ // Each processed child can only go in one direction along each axis, so check if the opposite side has been
106
+ // traversed.
107
+ if (process_chunk.traversed & (1 << (u16 )opposite_face)) continue ;
78
108
79
109
s32 chunk_x = process_chunk.chunk_x + offset.x ;
80
110
s32 chunk_y = process_chunk.chunk_y + offset.y ;
@@ -85,42 +115,54 @@ void ChunkConnectivityGraph::Update(MemoryArena& trans_arena, World& world, cons
85
115
size_t new_x_index = world::GetChunkCacheIndex (chunk_x);
86
116
size_t new_z_index = world::GetChunkCacheIndex (chunk_z);
87
117
88
- if (!world.chunk_infos [new_z_index][new_x_index].loaded ) continue ;
118
+ VisitState* visit_state =
119
+ visit_states + chunk_y * kChunkCacheSize * kChunkCacheSize + new_z_index * kChunkCacheSize + new_x_index;
120
+ if (!visit_state->CanVisit (through_face)) continue ;
121
+
122
+ ChunkSectionInfo& new_info = world.chunk_infos [new_z_index][new_x_index];
123
+
124
+ if (!new_info.loaded ) continue ;
89
125
90
- if ((process_index == 0 && connect_set.HasFaceConnectivity (through_face)) ||
91
- connect_set.IsConnected ((BlockFace)process_chunk.from , through_face)) {
92
- BlockFace from = GetOppositeFace (through_face);
126
+ // Always travel through camera-connected chunks.
127
+ bool is_camera_connected = (process_chunk.from == 0xFF && connect_set.HasFaceConnectivity (through_face));
128
+ // If we can go from the 'from' side that reached here to the new through-side, then we might be able to see
129
+ // through this chunk.
130
+ bool visibility_potential =
131
+ process_chunk.from != 0xFF && connect_set.IsConnected (through_face, (BlockFace)process_chunk.from );
93
132
94
- size_t view_index = (size_t )new_z_index * kChunkCacheSize * kChunkColumnCount +
95
- (size_t )new_x_index * kChunkColumnCount + chunk_y;
96
- if (!view_set.test (view_index)) {
97
- view_set.set (view_index);
133
+ if (is_camera_connected || visibility_potential) {
134
+ Vector3f chunk_min (chunk_x * 16 .0f , chunk_y * 16 .0f - 64 .0f , chunk_z * 16 .0f );
135
+ Vector3f chunk_max (chunk_x * 16 .0f + 16 .0f , chunk_y * 16 .0f - 48 .0f , chunk_z * 16 .0f + 16 .0f );
98
136
99
- Vector3f chunk_min (chunk_x * 16 .0f , chunk_y * 16 .0f - 64 .0f , chunk_z * 16 .0f );
100
- Vector3f chunk_max (chunk_x * 16 .0f + 16 .0f , chunk_y * 16 .0f - 48 .0f , chunk_z * 16 .0f + 16 .0f );
137
+ if (frustum.Intersects (chunk_min, chunk_max)) {
138
+ size_t view_index = (size_t )new_z_index * kChunkCacheSize * kChunkColumnCount +
139
+ (size_t )new_x_index * kChunkColumnCount + chunk_y;
140
+
141
+ // Only add each chunk to the visibility set once.
142
+ if (!view_set.test (view_index)) {
143
+ view_set.set (view_index);
101
144
102
- if (frustum.Intersects (chunk_min, chunk_max)) {
103
145
if (world.chunk_infos [new_z_index][new_x_index].bitmask & (1 << chunk_y)) {
104
146
VisibleChunk* next_chunk = visible_set + visible_count++;
105
147
106
148
next_chunk->chunk_x = chunk_x;
107
149
next_chunk->chunk_y = chunk_y;
108
150
next_chunk->chunk_z = chunk_z;
109
151
}
152
+ }
110
153
111
- BlockFace opposite_face = GetOppositeFace (through_face);
112
-
113
- if (!(process_chunk.dirs & (1 << (u16 )opposite_face))) {
114
- trans_arena.Allocate (sizeof (ProcessChunk), 1 );
115
- process_queue[process_queue_size++] = {chunk_x, chunk_y, chunk_z, (u16 )from,
116
- (u16 )(process_chunk.dirs | (1 << i))};
117
- }
154
+ if (process_queue_size >= largest_queue_size) {
155
+ largest_queue_size = process_queue_size + 1 ;
156
+ trans_arena.Allocate (sizeof (ProcessChunk), 1 );
118
157
}
158
+
159
+ u16 new_traversed = process_chunk.traversed | (1 << (u16 )through_face);
160
+ process_queue[process_queue_size++] = {chunk_x, chunk_y, chunk_z, (u16 )opposite_face, new_traversed};
161
+
162
+ visit_state->VisitThrough (through_face);
119
163
}
120
164
}
121
165
}
122
-
123
- ++process_index;
124
166
}
125
167
}
126
168
@@ -155,7 +197,7 @@ bool ChunkConnectivitySet::Build(const World& world, const Chunk& chunk) {
155
197
156
198
if (!(model.element_count == 0 || model.HasTransparency ())) continue ;
157
199
158
- if (!visited.test ((size_t )z * 16 * 16 + (size_t )y * 16 + (size_t )x)) {
200
+ if (!visited.test ((size_t )y * 16 * 16 + (size_t )z * 16 + (size_t )x)) {
159
201
u8 current_set = FloodFill (world, chunk, visited, queue, x, y, z);
160
202
161
203
for (size_t i = 0 ; i < 6 ; ++i) {
@@ -188,7 +230,7 @@ u8 ChunkConnectivitySet::FloodFill(const World& world, const Chunk& chunk, Visit
188
230
189
231
u8 current_set = 0 ;
190
232
191
- queue_set.set ((size_t )(start_z ) * 16 * 16 + (size_t )(start_y ) * 16 + (size_t )(start_x));
233
+ queue_set.set ((size_t )(start_y ) * 16 * 16 + (size_t )(start_z ) * 16 + (size_t )(start_x));
192
234
193
235
while (queue_index < queue_count) {
194
236
s8 x = queue[queue_index].x ;
@@ -224,37 +266,37 @@ u8 ChunkConnectivitySet::FloodFill(const World& world, const Chunk& chunk, Visit
224
266
}
225
267
}
226
268
227
- if (!visited.test ((size_t )z * 16 * 16 + (size_t )y * 16 + (size_t )x)) {
228
- visited.set ((size_t )z * 16 * 16 + (size_t )y * 16 + (size_t )x);
269
+ if (!visited.test ((size_t )y * 16 * 16 + (size_t )z * 16 + (size_t )x)) {
270
+ visited.set ((size_t )y * 16 * 16 + (size_t )z * 16 + (size_t )x);
229
271
230
- if (x > 0 && !queue_set.test ((size_t )(z ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x - 1 ))) {
272
+ if (x > 0 && !queue_set.test ((size_t )(y ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x - 1 ))) {
231
273
queue[queue_count++] = {(s8)(x - 1 ), y, z};
232
- queue_set.set ((size_t )(z ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x - 1 ));
274
+ queue_set.set ((size_t )(y ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x - 1 ));
233
275
}
234
276
235
- if (x < 15 && !queue_set.test ((size_t )(z ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x + 1 ))) {
277
+ if (x < 15 && !queue_set.test ((size_t )(y ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x + 1 ))) {
236
278
queue[queue_count++] = {(s8)(x + 1 ), y, z};
237
- queue_set.set ((size_t )(z ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x + 1 ));
279
+ queue_set.set ((size_t )(y ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x + 1 ));
238
280
}
239
281
240
- if (y > 0 && !queue_set.test ((size_t )(z ) * 16 * 16 + (size_t )(y - 1 ) * 16 + (size_t )(x))) {
282
+ if (y > 0 && !queue_set.test ((size_t )(y - 1 ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x))) {
241
283
queue[queue_count++] = {x, (s8)(y - 1 ), z};
242
- queue_set.set ((size_t )(z ) * 16 * 16 + (size_t )(y - 1 ) * 16 + (size_t )(x));
284
+ queue_set.set ((size_t )(y - 1 ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x));
243
285
}
244
286
245
- if (y < 15 && !queue_set.test ((size_t )(z ) * 16 * 16 + (size_t )(y + 1 ) * 16 + (size_t )(x))) {
287
+ if (y < 15 && !queue_set.test ((size_t )(y + 1 ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x))) {
246
288
queue[queue_count++] = {x, (s8)(y + 1 ), z};
247
- queue_set.set ((size_t )(z ) * 16 * 16 + (size_t )(y + 1 ) * 16 + (size_t )(x));
289
+ queue_set.set ((size_t )(y + 1 ) * 16 * 16 + (size_t )(z ) * 16 + (size_t )(x));
248
290
}
249
291
250
- if (z > 0 && !queue_set.test ((size_t )(z - 1 ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x))) {
292
+ if (z > 0 && !queue_set.test ((size_t )(y ) * 16 * 16 + (size_t )(z - 1 ) * 16 + (size_t )(x))) {
251
293
queue[queue_count++] = {x, y, (s8)(z - 1 )};
252
- queue_set.set ((size_t )(z - 1 ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x));
294
+ queue_set.set ((size_t )(y ) * 16 * 16 + (size_t )(z - 1 ) * 16 + (size_t )(x));
253
295
}
254
296
255
- if (z < 15 && !queue_set.test ((size_t )(z + 1 ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x))) {
297
+ if (z < 15 && !queue_set.test ((size_t )(y ) * 16 * 16 + (size_t )(z + 1 ) * 16 + (size_t )(x))) {
256
298
queue[queue_count++] = {x, y, (s8)(z + 1 )};
257
- queue_set.set ((size_t )(z + 1 ) * 16 * 16 + (size_t )(y ) * 16 + (size_t )(x));
299
+ queue_set.set ((size_t )(y ) * 16 * 16 + (size_t )(z + 1 ) * 16 + (size_t )(x));
258
300
}
259
301
}
260
302
}
0 commit comments