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