1 /**
2 Copyright: Copyright (c) 2015-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.storage.worldbox;
7 
8 import voxelman.log;
9 import voxelman.math;
10 import voxelman.graphics;
11 
12 import voxelman.core.config;
13 import voxelman.world.storage.coordinates;
14 
15 
16 void putCube(ref Batch batch, Box box, Color4ub color, bool fill, bool offset = true)
17 {
18 	vec3 pos = box.position;
19 	vec3 size = box.size;
20 	if (offset) {
21 		pos -= vec3(0.01, 0.01, 0.01);
22 		size += vec3(0.02, 0.02, 0.02);
23 	}
24 	batch.putCube(pos, size, color, fill);
25 }
26 
27 WorldBox shiftAndClampBoxByBorders(WorldBox box, Box dimBorders)
28 {
29 	if (box.position.x < dimBorders.position.x)
30 		box.position.x = dimBorders.position.x;
31 	if (box.position.y < dimBorders.position.y)
32 		box.position.y = dimBorders.position.y;
33 	if (box.position.z < dimBorders.position.z)
34 		box.position.z = dimBorders.position.z;
35 
36 	if (box.endPosition.x > dimBorders.endPosition.x)
37 		box.position.x = dimBorders.endPosition.x - box.size.x + 1;
38 	if (box.endPosition.y > dimBorders.endPosition.y)
39 		box.position.y = dimBorders.endPosition.y - box.size.y + 1;
40 	if (box.endPosition.z > dimBorders.endPosition.z)
41 		box.position.z = dimBorders.endPosition.z - box.size.z + 1;
42 
43 	return WorldBox(boxIntersection(box, dimBorders), box.dimension);
44 }
45 
46 WorldBox calcBox(ChunkWorldPos cwp, int viewRadius)
47 {
48 	int size = viewRadius*2 + 1;
49 	return WorldBox(cast(ivec3)(cwp.ivector3 - viewRadius),
50 		ivec3(size, size, size), cwp.w);
51 }
52 
53 WorldBox worldBoxFromCorners(ivec3 a, ivec3 b, ushort dimension)
54 {
55 	return WorldBox(boxFromCorners(a, b), dimension);
56 }
57 
58 WorldBox blockBoxToChunkBox(WorldBox blockBox)
59 {
60 	auto startPosition = blockToChunkPosition(blockBox.position);
61 	auto endPosition = blockToChunkPosition(blockBox.endPosition);
62 	return worldBoxFromCorners(startPosition, endPosition, blockBox.dimension);
63 }
64 
65 // makes block box in chunk-local space out of world space
66 WorldBox blockBoxToChunkLocalBox(WorldBox blockBox)
67 {
68 	blockBox.position -= chunkStartBlockPos(blockBox.position);
69 	return blockBox;
70 }
71 
72 /// Returns chunks if their mesh may have changed after specified modification
73 /// WorldBox is specified in block space
74 WorldBox calcModifiedMeshesBox(WorldBox modificationBox)
75 {
76 	// We increase size by 1 in every direction.
77 	// After rounding chunks on the border of modification will be included
78 	WorldBox expandedBox = modificationBox;
79 	expandedBox.position -= ivec3(1,1,1);
80 	expandedBox.size += ivec3(2,2,2);
81 	WorldBox chunkBox = blockBoxToChunkBox(expandedBox);
82 	return chunkBox;
83 }
84 
85 WorldBox chunkToBlockBox(ChunkWorldPos cwp) {
86 	return chunkToBlockBox(cwp.ivector3, cwp.dimension);
87 }
88 
89 WorldBox chunkToBlockBox(ivec3 cwp, ushort dimension) {
90 	ivec3 startPosition = chunkToBlockPosition(cwp);
91 	return WorldBox(startPosition, CHUNK_SIZE_VECTOR, dimension);
92 }
93 
94 Box chunkToBlockBox(ivec3 cwp) {
95 	ivec3 startPosition = chunkToBlockPosition(cwp);
96 	return Box(startPosition, CHUNK_SIZE_VECTOR);
97 }
98 
99 struct WorldBox
100 {
101 	Box box;
102 	alias box this;
103 	ushort dimension;
104 
105 	this(ivec3 pos, ivec3 size, ushort dim)
106 	{
107 		box = Box(pos, size);
108 		dimension = dim;
109 	}
110 
111 	this(Box box, ushort dim)
112 	{
113 		this.box = box;
114 		dimension = dim;
115 	}
116 
117 	bool contains(ivec3 point, ushort dimension) const
118 	{
119 		if (this.dimension != dimension) return false;
120 		return box.contains(point);
121 	}
122 
123 	import std.algorithm : cartesianProduct, map, joiner, equal, canFind;
124 	import std.range : iota, walkLength;
125 	import std.array : array;
126 
127 	/// iterate all posisions within a box
128 	int opApply(scope int delegate(ivec4) del) {
129 		foreach (y; position.y .. position.y + size.y)
130 		foreach (z; position.z .. position.z + size.z)
131 		foreach (x; position.x .. position.x + size.x)
132 			if (auto ret = del(ivec4(x, y, z, dimension)))
133 				return ret;
134 		return 0;
135 	}
136 
137 	/// ditto
138 	int opApply(scope int delegate(ChunkWorldPos) del) {
139 		foreach (y; position.y .. position.y + size.y)
140 		foreach (z; position.z .. position.z + size.z)
141 		foreach (x; position.x .. position.x + size.x)
142 			if (auto ret = del(ChunkWorldPos(x, y, z, dimension)))
143 				return ret;
144 		return 0;
145 	}
146 
147 	WorldBox intersection(WorldBox other) {
148 		return worldBoxIntersection(this, other);
149 	}
150 
151 	WorldBox intersection(Box other) {
152 		return WorldBox(boxIntersection(this, other), dimension);
153 	}
154 
155 	bool opEquals()(auto const ref WorldBox other) const
156 	{
157 		return box == other.box && dimension == other.dimension;
158 	}
159 }
160 
161 TrisectResult trisect4d(WorldBox a, WorldBox b)
162 {
163 	WorldBox intersection = worldBoxIntersection(a, b);
164 
165 	// no intersection
166 	if (intersection.empty)
167 	{
168 		return TrisectResult([a], Box(), [b]);
169 	}
170 
171 	auto result = trisectIntersecting(a, b);
172 	result.intersection = intersection;
173 	return result;
174 }
175 
176 unittest
177 {
178 	assert(WorldBox(Box(), 0) != WorldBox(Box(), 1));
179 	assert(WorldBox(Box(), 0) == WorldBox(Box(), 0));
180 }
181 
182 WorldBox worldBoxIntersection(WorldBox a, WorldBox b)
183 {
184 	if (a.dimension != b.dimension)
185 	{
186 		return WorldBox();
187 	}
188 
189 	auto box = boxIntersection(a.box, b.box);
190 	return WorldBox(box, a.dimension);
191 }