1 /*
2 * @file
3 * @brief Based on the BSP_tool from botman's Half-Life BSP utilities
4 */
5 #ifndef ANDROID
6 #include "bspslicer.h"
7 #include "../ports/system.h"
8 #include "../common/common.h"
9 #include "../shared/images.h"
10
11 #define DEPTH 3
12
SL_CreatePNGFile(const char * filename,unsigned char * buffer,int width,int height)13 static bool SL_CreatePNGFile (const char* filename, unsigned char* buffer, int width, int height)
14 {
15 ScopedFile f;
16
17 /* create the .bmp file... */
18 if (FS_OpenFile(filename, &f, FILE_WRITE) == -1)
19 return false;
20
21 R_WritePNG(&f, buffer, width, height);
22
23 return true;
24 }
25
26 #define IMAGE_BUFFER_SET_BLACK(buffer, offset) do {int i; for (i = 0; i < DEPTH; i++) {buffer[offset + i] = 0xFF; } } while(0);
27
SL_Bresenham(int x0,int y0,int x1,int y1,int width,int height,bool rotated,unsigned char * outputBuffer)28 static void SL_Bresenham (int x0, int y0, int x1, int y1, int width, int height, bool rotated, unsigned char* outputBuffer)
29 {
30 int dy = y1 - y0;
31 int dx = x1 - x0;
32 int stepx, stepy;
33 int offset;
34 const int maxOffset = width * height * DEPTH;
35
36 if (dy < 0) {
37 dy = -dy;
38 stepy = -1;
39 } else {
40 stepy = 1;
41 }
42 if (dx < 0) {
43 dx = -dx;
44 stepx = -1;
45 } else {
46 stepx = 1;
47 }
48 dy *= 2;
49 dx *= 2;
50
51 if (rotated)
52 offset = x0 * DEPTH * height + y0;
53 else
54 offset = y0 * width * DEPTH + x0 * DEPTH;
55
56 if (offset >= 0 && offset < maxOffset) {
57 IMAGE_BUFFER_SET_BLACK(outputBuffer, offset);
58 } else
59 return;
60
61 if (dx > dy) {
62 int fraction = 2 * dy - (dx >> 1); /* same as 2*dy - dx */
63 while (x0 != x1) {
64 if (fraction >= 0) {
65 y0 += stepy;
66 fraction -= dx; /* same as fraction -= 2*dx */
67 }
68 x0 += stepx;
69 fraction += dy; /* same as fraction -= 2*dy */
70
71 if (rotated)
72 offset = x0 * DEPTH * height + y0;
73 else
74 offset = y0 * width * DEPTH + x0 * DEPTH;
75
76 if (offset >= 0 && offset < maxOffset) {
77 IMAGE_BUFFER_SET_BLACK(outputBuffer, offset);
78 } else
79 return;
80 }
81 } else {
82 int fraction = dx - (dy >> 1);
83 while (y0 != y1) {
84 if (fraction >= 0) {
85 x0 += stepx;
86 fraction -= dy;
87 }
88 y0 += stepy;
89 fraction += dx;
90
91 if (rotated)
92 offset = x0 * DEPTH * height + y0;
93 else
94 offset = y0 * width * DEPTH + x0 * DEPTH;
95
96 if (offset >= 0 && offset < maxOffset) {
97 IMAGE_BUFFER_SET_BLACK(outputBuffer, offset);
98 } else
99 return;
100 }
101 }
102 }
103
104 /**
105 * @note the "vector" parameter should be NORMALIZED!!!
106 */
SL_DistanceToIntersection(const vec3_t origin,const vec3_t vector,const vec3_t planeOrigin,const vec3_t planeNormal)107 static float SL_DistanceToIntersection (const vec3_t origin, const vec3_t vector, const vec3_t planeOrigin, const vec3_t planeNormal)
108 {
109 const float d = -(DotProduct(planeNormal, planeOrigin));
110 const float numerator = DotProduct(planeNormal, origin) + d;
111 const float denominator = DotProduct(planeNormal, vector);
112
113 if (fabs(denominator) < 0.00001)
114 /* normal is orthogonal to vector, no intersection */
115 return -1.0f;
116
117 return -(numerator / denominator);
118 }
119
120 /**
121 * @return @c true or false if vector intersects a plane...
122 */
SL_VectorIntersectPlane(const vec3_t origin,const vec3_t vector,const vec3_t planeOrigin,const vec3_t planeNormal,vec3_t intersectPoint)123 static bool SL_VectorIntersectPlane (const vec3_t origin, const vec3_t vector, const vec3_t planeOrigin, const vec3_t planeNormal, vec3_t intersectPoint)
124 {
125 vec3_t v_temp;
126 const float dist = SL_DistanceToIntersection(origin, vector, planeOrigin, planeNormal);
127 if (dist < 0)
128 return false;
129
130 VectorScale(vector, dist, v_temp);
131 VectorAdd(origin, v_temp, intersectPoint);
132
133 return true;
134 }
135
136 /**
137 * @brief slice the world in Z planes going from min_z to max_z by stepsize...
138 */
SL_SliceTheWorld(const TR_TILE_TYPE * tile,const vec3_t mins,const vec3_t maxs,float stepsize,int scale,bool singleContour,bool multipleContour)139 static void SL_SliceTheWorld (const TR_TILE_TYPE *tile, const vec3_t mins, const vec3_t maxs, float stepsize, int scale, bool singleContour, bool multipleContour)
140 {
141 int sliceIndex;
142 vec3_t zDistance = { 0.0f, 0.0f, 0.0f };
143 const vec3_t zNormal = { 0.0f, 0.0f, 1.0f };
144 vec3_t v[2];
145 vec3_t vTemp, intersectPoint;
146 float lineX1, lineY1, lineX2, lineY2;
147 bool first, both;
148 char filename[MAX_QPATH];
149 const float minX = mins[0];
150 const float maxX = maxs[0];
151 const float minY = mins[1];
152 const float maxY = maxs[1];
153 const float minZ = mins[2];
154 const float maxZ = maxs[2];
155
156 const int numberOfSlices = (int) ((maxZ - minZ) / stepsize);
157 int width;
158 int height;
159 bool rotated = false;
160 unsigned char* pictureBuffer;
161
162 lineX1 = lineY1 = lineX2 = lineY2 = 0.0f;
163
164 width = (int) (maxX - minX);
165 height = (int) (maxY - minY);
166
167 /* is the map higher than it is wide? */
168 if (height > width) {
169 rotated = true; /* rotate the map 90 degrees */
170 width = (int) (maxY - minY); /* swap the width and height */
171 height = (int) (maxX - minX);
172 }
173
174 height /= scale;
175 width /= scale;
176
177 pictureBuffer = Mem_AllocTypeN(unsigned char, width * height * DEPTH);
178
179 for (sliceIndex = 0; sliceIndex < numberOfSlices; sliceIndex++) {
180 int j;
181 const float zHeight = minZ + (sliceIndex * stepsize);
182 zDistance[2] = zHeight;
183
184 /* if not doing a contour map then clear the buffer each pass... */
185 if (!singleContour && !multipleContour)
186 memset(pictureBuffer, 0, width * height * DEPTH);
187
188 /* loop through all the faces of the BSP file... */
189 for (j = 0; j < tile->nummodels; j++) {
190 const dBspModel_t* model = &tile->models[j];
191 for (int faceIndex = 0; faceIndex < model->numfaces; faceIndex++) {
192 const dBspSurface_t* face = &tile->faces[model->firstface + faceIndex];
193 const dBspTexinfo_t* texinfo = &tile->texinfo[face->texinfo];
194 if (texinfo->surfaceFlags & (SURF_NODRAW|SURF_HINT|SURF_SKIP|SURF_BLEND33|SURF_BLEND66))
195 continue;
196 /** @todo handle content flags */
197
198 first = true;
199 both = false;
200
201 /* for each face, loop though all of the edges, getting the vertexes... */
202 for (int edgeIndex = 0; edgeIndex < face->numedges; edgeIndex++) {
203 /* get the coordinates of the vertex of this edge... */
204 int edge = tile->surfedges[face->firstedge + edgeIndex];
205
206 if (edge < 0) {
207 edge = -edge;
208 const dBspEdge_t* e = &tile->edges[edge];
209 v[0][0] = tile->vertexes[e->v[1]].point[0];
210 v[0][1] = tile->vertexes[e->v[1]].point[1];
211 v[0][2] = tile->vertexes[e->v[1]].point[2];
212 } else {
213 const dBspEdge_t* e = &tile->edges[edge];
214 v[0][0] = tile->vertexes[e->v[0]].point[0];
215 v[0][1] = tile->vertexes[e->v[0]].point[1];
216 v[0][2] = tile->vertexes[e->v[0]].point[2];
217 }
218
219 /* get the coordinates of the vertex of the next edge... */
220 edge = tile->surfedges[face->firstedge
221 + ((edgeIndex + 1) % face->numedges)];
222
223 if (edge < 0) {
224 edge = -edge;
225 const dBspEdge_t* e = &tile->edges[edge];
226 v[1][0] = tile->vertexes[e->v[1]].point[0];
227 v[1][1] = tile->vertexes[e->v[1]].point[1];
228 v[1][2] = tile->vertexes[e->v[1]].point[2];
229 } else {
230 const dBspEdge_t* e = &tile->edges[edge];
231 v[1][0] = tile->vertexes[e->v[0]].point[0];
232 v[1][1] = tile->vertexes[e->v[0]].point[1];
233 v[1][2] = tile->vertexes[e->v[0]].point[2];
234 }
235
236 /* vector from v[0] to v[1] intersect the Z plane? */
237 if (((v[0][2] < zHeight) && (v[1][2] > zHeight))
238 || ((v[1][2] < zHeight) && (v[0][2] > zHeight))) {
239 /* create a normalized vector between the 2 vertexes... */
240 VectorSubtract(v[1], v[0], vTemp);
241 VectorNormalize(vTemp);
242
243 /* find the point where the vector intersects the Z plane... */
244 SL_VectorIntersectPlane(v[0], vTemp, zDistance,
245 zNormal, intersectPoint);
246
247 /* is this the first or second Z plane intersection point? */
248 if (first) {
249 first = false;
250 lineX1 = intersectPoint[0];
251 lineY1 = intersectPoint[1];
252 } else {
253 both = true;
254 lineX2 = intersectPoint[0];
255 lineY2 = intersectPoint[1];
256 }
257 }
258
259 /* move v[1] to v[0] */
260 VectorCopy(v[1], v[0]);
261 }
262
263 /* did we find 2 points that intersected the Z plane? */
264 if (both) {
265 /* offset by min_x to start at 0 */
266 lineX1 -= minX;
267 lineX2 -= minX;
268 /* offset by min_y to start at 0 */
269 lineY1 -= minY;
270 lineY2 -= minY;
271
272 lineX1 = lineX1 / scale;
273 lineY1 = lineY1 / scale;
274 lineX2 = lineX2 / scale;
275 lineY2 = lineY2 / scale;
276
277 /* are these points within the bounding box of the world? */
278 if (lineX1 >= 0 && lineX1 < width && lineY1 >= 0 && lineY1 < height
279 && lineX2 >= 0 && lineX2 < width && lineY2 >= 0 && lineY2 < height) {
280 const int x1 = (int) lineX1;
281 const int y1 = (int) lineY1;
282 const int x2 = (int) lineX2;
283 const int y2 = (int) lineY2;
284
285 if (rotated)
286 SL_Bresenham(x1, height - y1, x2, height - y2, width, height, rotated, pictureBuffer);
287 else
288 SL_Bresenham(x1, y1, x2, y2, width, height, rotated, pictureBuffer);
289 }
290 }
291 }
292 }
293
294 if (!singleContour) {
295 Com_sprintf(filename, sizeof(filename), "out%d.png", sliceIndex + 1);
296 Com_Printf("creating %s...\n", filename);
297 SL_CreatePNGFile(filename, pictureBuffer, width, height);
298 }
299 }
300
301 if (singleContour) {
302 Q_strncpyz(filename, "out.png", sizeof(filename));
303 Com_Printf("creating %s...\n", filename);
304 SL_CreatePNGFile(filename, pictureBuffer, width, height);
305 }
306
307 Mem_Free(pictureBuffer);
308 }
309
310 /**
311 * @param[in] thickness the thickness of the brushes to render to the 2d map
312 */
SL_BSPSlice(const TR_TILE_TYPE * model,float thickness,int scale,bool singleContour,bool multipleContour)313 void SL_BSPSlice (const TR_TILE_TYPE *model, float thickness, int scale, bool singleContour, bool multipleContour)
314 {
315 int i;
316 /** @todo remove these values once the mins/maxs calculation works */
317 vec3_t mins = {-MAX_WORLD_WIDTH, -MAX_WORLD_WIDTH, 0};
318 vec3_t maxs = {MAX_WORLD_WIDTH, MAX_WORLD_WIDTH, (PATHFINDING_HEIGHT - 1) * UNIT_HEIGHT};
319
320 if (model == nullptr)
321 return;
322
323 for (i = 0; i < model->nummodels; i++) {
324 const dBspModel_t* d = &model->models[i];
325 AddPointToBounds(d->mins, mins, maxs);
326 AddPointToBounds(d->maxs, mins, maxs);
327 }
328
329 /* don't start or end on exact multiples of the Z slice thickness
330 * (if you do, it causes "weirdness" in the plane intersect function) */
331
332 /* offset a tiny bit */
333 mins[2] = (float) floor(mins[2]) + 0.01f;
334 maxs[2] = (float) floor(maxs[2]) + 0.01f;
335
336 SL_SliceTheWorld(model, mins, maxs, thickness, scale, singleContour, multipleContour);
337 }
338
339 #endif
340