1 /** \file map.cc
2    Handles all information about the current map, including the individual cells.
3 */
4 /*
5    Copyright (C) 2000  Mathias Broxvall
6                        Yannick Perret
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22 
23 #include "map.h"
24 #include "editMode.h"
25 #include "game.h"
26 
27 #include <SDL2/SDL_endian.h>
28 #include <SDL2/SDL_image.h>
29 #include <zlib.h>
30 #include <cstdlib>
31 
32 /* VISRADIUS is half-width of square of drawable cells */
33 #define VISRADIUS 50
34 #define CHUNKSIZE 32
35 
36 #define WRITE_PORTABLE 1
37 #define READ_PORTABLE 1
38 
39 const int Map::flagNone = 0, Map::flagFlashCenter = 1, Map::flagTranslucent = 2,
40           Map::flagShowCross = 4;
41 const int Map::mapFormatVersion = 7;
42 
saveInt(int32_t v)43 inline int32_t saveInt(int32_t v) { return (int32_t)SDL_SwapBE32((uint32_t)v); }
loadInt(int32_t v)44 inline int32_t loadInt(int32_t v) { return (int32_t)SDL_SwapBE32((uint32_t)v); }
45 
46 /* initialization of some fields to be sure... */
Cell()47 Cell::Cell() {
48   texture = -1;
49   flags = 0;
50   for (int i = 0; i < 5; i++) colors[i] = Color(1., 1., 1., 1.);
51   for (int i = 0; i < 4; i++) wallColors[i] = Color(1., 1., 1., 1.);
52   for (int i = 0; i < 5; i++) heights[i] = -8.0;
53   for (int i = 0; i < 5; i++) waterHeights[i] = -20.0;
54   velocity[0] = 0.;
55   velocity[1] = 1.;
56   sunken = 0.;
57   displayListDirty = false;
58 }
59 
calcNormals(const float heights[5],Coord3d normals[5])60 static void calcNormals(const float heights[5], Coord3d normals[5]) {
61   Coord3d spines[4] = {
62       Coord3d(-0.5, -0.5, heights[Cell::SW] - heights[Cell::CENTER]),
63       Coord3d(0.5, -0.5, heights[Cell::SE] - heights[Cell::CENTER]),
64       Coord3d(0.5, 0.5, heights[Cell::NE] - heights[Cell::CENTER]),
65       Coord3d(-0.5, 0.5, heights[Cell::NW] - heights[Cell::CENTER]),
66   };
67 
68   Coord3d faceNormals[4];
69   for (int i = 0; i < 4; i++) {
70     faceNormals[i] = crossProduct(spines[i], spines[(i + 1) % 4]);
71     faceNormals[i] = faceNormals[i] / length(faceNormals[i]);
72   }
73   for (int i = 0; i < 5; i++) { normals[i] = Coord3d(); }
74   for (int i = 0; i < 4; i++) {
75     normals[i] = normals[i] + faceNormals[i];
76     normals[i] = normals[i] + faceNormals[(i + 3) % 4];
77     normals[4] = normals[4] + faceNormals[i];
78   }
79   for (int i = 0; i < 5; i++) { normals[i] = normals[i] / length(normals[i]); }
80 }
81 
82 /* Returns the average normal at a vertex of the cell */
getNormals(Coord3d normals[5]) const83 void Cell::getNormals(Coord3d normals[5]) const { calcNormals(heights, normals); }
84 /* Works on water heights */
getWaterNormals(Coord3d normals[5]) const85 void Cell::getWaterNormals(Coord3d normals[5]) const { calcNormals(waterHeights, normals); }
86 
calcHeight(const float heights[5],Real x,Real y)87 static Real calcHeight(const float heights[5], Real x, Real y) {
88   x = x - 0.5f;
89   y = y - 0.5f;
90 
91   Real rp = std::abs(x + y);
92   Real rm = std::abs(x - y);
93   Real ph = y > -x ? heights[Cell::NE] : heights[Cell::SW];
94   Real mh = y > x ? heights[Cell::NW] : heights[Cell::SE];
95   Real ch = heights[Cell::CENTER];
96 
97   return ch * (1 - rp - rm) + ph * rp + mh * rm;
98 }
99 
100 /* Gives the height of the cell in a specified (floatingpoint) position */
getHeight(Real x,Real y) const101 Real Cell::getHeight(Real x, Real y) const { return calcHeight(heights, x, y); }
102 
103 /* Works for water */
getWaterHeight(Real x,Real y) const104 Real Cell::getWaterHeight(Real x, Real y) const { return calcHeight(waterHeights, x, y); }
105 
Chunk()106 Chunk::Chunk() {
107   is_active = false;
108   is_visible = false;
109   is_updated = true;
110   last_shown = -1;
111   // Init with extreme range to ensure visibility after which
112   // exact range would be calculated
113   maxHeight = 1e3;
114   minHeight = 1e-3;
115   xm = 0;
116   ym = 0;
117 }
118 
~Chunk()119 Chunk::~Chunk() {
120   if (is_active) {
121     glDeleteBuffers(2, &tile_vbo[0]);
122     glDeleteBuffers(2, &flag_vbo[0]);
123     glDeleteBuffers(2, &flui_vbo[0]);
124     glDeleteBuffers(2, &wall_vbo[0]);
125     glDeleteBuffers(2, &line_vbo[0]);
126     glDeleteVertexArrays(1, &tile_vao);
127     glDeleteVertexArrays(1, &flag_vao);
128     glDeleteVertexArrays(1, &flui_vao);
129     glDeleteVertexArrays(1, &wall_vao);
130     glDeleteVertexArrays(1, &line_vao);
131   }
132 }
133 
createTextureArray(const char ** texs,int ntex,int size)134 static GLuint createTextureArray(const char** texs, int ntex, int size) {
135   GLuint texture_Array = 0;
136   glGenTextures(1, &texture_Array);
137   glBindTexture(GL_TEXTURE_2D_ARRAY, texture_Array);
138   char* data = new char[ntex * size * size * 4];
139   for (int i = 0; i < ntex; i++) {
140     /* loadImage aborts with error if any of the above textures DNE */
141     SDL_Surface* orig = loadImage(texs[i]);
142     Uint32 mask[4];
143 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
144     mask[0] = 0x000000FF;
145     mask[1] = 0x0000FF00;
146     mask[2] = 0x00FF0000;
147     mask[3] = 0xFF000000;
148 #else
149     mask[0] = 0xFF000000;
150     mask[1] = 0x00FF0000;
151     mask[2] = 0x0000FF00;
152     mask[3] = 0x000000FF;
153 #endif
154     SDL_Surface* proj = SDL_CreateRGBSurface(SDL_SWSURFACE, size, size, 32, mask[0], mask[1],
155                                              mask[2], mask[3]);
156     SDL_Rect orect = {0, 0, orig->w, orig->h};
157     SDL_Rect drect = {0, 0, size, size};
158     SDL_SetSurfaceBlendMode(orig, SDL_BLENDMODE_NONE);
159     SDL_BlitScaled(orig, &orect, proj, &drect);
160     SDL_FreeSurface(orig);
161     SDL_LockSurface(proj);
162     memcpy(&data[i * size * size * 4], proj->pixels, size * size * 4);
163     SDL_UnlockSurface(proj);
164     SDL_FreeSurface(proj);
165   }
166   glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, size, size, ntex, 0, GL_RGBA,
167                GL_UNSIGNED_BYTE, data);
168   delete[] data;
169 
170   glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
171   glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
172   glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
173   glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
174 
175   glBindTexture(GL_TEXTURE_2D_ARRAY, texture_Array);
176   return texture_Array;
177 }
178 
Map(char * filename)179 Map::Map(char* filename) {
180   gzFile gp;
181   int x, y, i;
182 
183   isBonus = 0;
184 
185   memset(mapname, 0, sizeof(mapname));
186   memset(author, 0, sizeof(author));
187   gp = gzopen(filename, "rb");
188   if (gp) {
189     int version;
190     int32_t data[6];
191     gzread(gp, data, sizeof(int32_t) * 6);
192     for (i = 0; i < 3; i++) startPosition[i] = 0.5 + loadInt(data[i]);
193     width = loadInt(data[3]);
194     height = loadInt(data[4]);
195     version = loadInt(data[5]);
196 
197     if (version < mapFormatVersion)
198       warning("Warning. Map %s is of an old format (v%d, latest is v%d)", filename, version,
199               mapFormatVersion);
200     else if (version > mapFormatVersion) {
201       error(
202           "Error. Map %s is from the future (v%d, I know only format v%d)\n"
203           "This error usually occurs because or broken maps or big/small endian issues",
204           filename, version, mapFormatVersion);
205     }
206 
207     if (version >= 7) { /* Read texture indices */
208       gzread(gp, data, sizeof(int32_t) * 1);
209       int nt = loadInt(data[0]);  // num textures used in map
210       char textureName[64];
211       for (i = 0; i < nt; i++) {
212         gzread(gp, textureName, 64);
213         indexTranslation[i] = loadTexture(textureName);
214       }
215     } else  // for old maps we just assume that all loaded textures are in the same order as
216             // from creator
217       for (i = 0; i < 256; i++) indexTranslation[i] = i;
218 
219     cells = new Cell[width * height];
220 
221     for (y = 0; y < height; y++)
222       for (x = 0; x < width; x++) {
223         // We have to do this here since Cell::load does not know it's own coordinates
224         Cell& c = cell(x, y);
225         c.load(this, gp, version);
226       }
227     gzclose(gp);
228   } else {
229     warning("could not open %s", filename);
230     width = height = 256;
231     cells = new Cell[width * height];
232     startPosition[0] = startPosition[1] = 252;
233     startPosition[2] = 0.0;
234     for (x = 0; x < width; x++)
235       for (y = 0; y < height; y++) {
236         Cell& c = cells[x + y * width];
237 
238         c.flags = 0;
239         for (i = 0; i < 5; i++) {
240           c.heights[i] = -8.0;
241           c.waterHeights[i] = -8.5;  // this is the groundwater =)
242           c.colors[i] = Color(0.9, 0.9, 0.9, 1.0);
243         }
244         for (i = 0; i < 4; i++) { c.wallColors[i] = Color(0.7, 0.2, 0.2, 1.0); }
245         c.velocity[0] = 0.0;
246         c.velocity[1] = 0.0;
247       }
248   }
249 
250   /* Fix display lists used by each cell */
251   for (y = 0; y < height; y++)
252     for (x = 0; x < width; x++) {
253       Cell& c = cells[x + y * width];
254       c.displayListDirty = true;
255     }
256 
257   flags = flagNone;
258   startPosition[2] = getHeight(startPosition[0], startPosition[1]);
259 
260   tx_Ice = loadTexture("ice.png");
261   tx_Acid = loadTexture("acid.png");
262   tx_Sand = loadTexture("sand.png");
263   tx_Track = loadTexture("track.png");
264   tx_1 = loadTexture("texture.png");
265   tx_2 = loadTexture("texture2.png");
266   tx_3 = loadTexture("texture3.png");
267   tx_4 = loadTexture("texture4.png");
268   tx_Water = loadTexture("water.png");
269 
270   chunks.clear();
271 
272   // Construct texture array
273   const int nsubtextures = 9;
274   const int size = 1 << 8;
275   const char* texs[nsubtextures] = {"blank.png",    "ice.png",      "sand.png",
276                                     "acid.png",     "track.png",    "texture.png",
277                                     "texture2.png", "texture3.png", "texture4.png"};
278   texture_Array = createTextureArray(texs, nsubtextures, size);
279 
280   const int nflags = 8;
281   const char* flags[nflags] = {"transparent.png",     "cell_multi.png", "cell_ice.png",
282                                "cell_sand.png",       "cell_track.png", "cell_acid.png",
283                                "cell_trampoline.png", "cell_kill.png"};
284   flag_Array = createTextureArray(flags, nflags, size);
285 }
~Map()286 Map::~Map() {
287   delete[] cells;
288   chunks.clear();
289 
290   glDeleteTextures(1, &texture_Array);
291   glDeleteTextures(1, &flag_Array);
292 }
293 
configureCellAttributes(bool water)294 static void configureCellAttributes(bool water) {
295   // Position
296   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)0);
297   // Color
298   glVertexAttribPointer(1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 8 * sizeof(GLfloat),
299                         (void*)(3 * sizeof(GLfloat)));
300   // Texture
301   if (water) {
302     glVertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, GL_TRUE, 8 * sizeof(GLfloat),
303                           (void*)(5 * sizeof(GLfloat)));
304   } else {
305     glVertexAttribPointer(2, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 8 * sizeof(GLfloat),
306                           (void*)(5 * sizeof(GLfloat)));
307   }
308   // Normal
309   glVertexAttribPointer(3, 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, 8 * sizeof(GLfloat),
310                         (void*)(6 * sizeof(GLfloat)));
311   // Velocity
312   glVertexAttribPointer(4, 2, GL_SHORT, GL_TRUE, 8 * sizeof(GLfloat),
313                         (void*)(7 * sizeof(GLfloat)));
314   glEnableVertexAttribArray(0);
315   glEnableVertexAttribArray(1);
316   glEnableVertexAttribArray(2);
317   glEnableVertexAttribArray(3);
318   glEnableVertexAttribArray(4);
319 }
320 
321 /* Draws the map on the screen from current viewpoint */
draw(int stage,const Coord3d & focus,GLfloat gameTime)322 void Map::draw(int stage, const Coord3d& focus, GLfloat gameTime) {
323   if (stage == 0) {
324     glDisable(GL_BLEND);
325   } else {
326     glEnable(GL_BLEND);
327     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
328   }
329 
330   int cx = (int)focus[0], cy = (int)focus[1];
331   int origx = cx - cx % CHUNKSIZE, origy = cy - cy % CHUNKSIZE;
332   int prad = (VISRADIUS / CHUNKSIZE) + 1;
333 
334   Matrix4d mvp;
335   matrixMult(activeView.modelview, activeView.projection, mvp);
336 
337   int nchunks = 0;
338   Chunk* drawlist[1024];
339   for (int i = -prad; i <= prad; i++) {
340     for (int j = -prad; j < +prad; j++) {
341       int hx = origx + i * CHUNKSIZE, hy = origy + j * CHUNKSIZE;
342       Chunk* cur = chunk(hx, hy);
343       int update = 0;
344       if (stage == 0) {
345         // Detect if update needed.
346         update = cur->is_updated;
347 
348         int visible = testBboxClip(cur->xm, cur->xm + CHUNKSIZE, cur->ym, cur->ym + CHUNKSIZE,
349                                    cur->minHeight, cur->maxHeight, mvp);
350 
351         // Current cell is in viewport
352         int ox = hx + CHUNKSIZE / 2;
353         int oy = hy + CHUNKSIZE / 2;
354         int inrad = (ox - cx) * (ox - cx) + (oy - cy) * (oy - cy) < 2 * VISRADIUS * VISRADIUS;
355         visible = visible && inrad;
356 
357         cur->is_visible = visible;
358       }
359       if (cur->is_visible) {
360         cur->last_shown = displayFrameNumber;
361         drawlist[nchunks] = cur;
362         if (update || !cur->is_active) { fillChunkVBO(drawlist[nchunks]); }
363         nchunks++;
364       } else {
365         // Cleanup buffers for zones that have long since dropped out of view
366         if (cur->is_active && cur->last_shown < displayFrameNumber - 10) {
367           glDeleteBuffers(2, &cur->tile_vbo[0]);
368           glDeleteBuffers(2, &cur->flag_vbo[0]);
369           glDeleteBuffers(2, &cur->flui_vbo[0]);
370           glDeleteBuffers(2, &cur->wall_vbo[0]);
371           glDeleteBuffers(2, &cur->line_vbo[0]);
372           glDeleteVertexArrays(1, &cur->tile_vao);
373           glDeleteVertexArrays(1, &cur->flag_vao);
374           glDeleteVertexArrays(1, &cur->flui_vao);
375           glDeleteVertexArrays(1, &cur->wall_vao);
376           glDeleteVertexArrays(1, &cur->line_vao);
377           cur->is_active = false;
378         }
379       }
380     }
381   }
382 
383   // Put into shader
384   setActiveProgramAndUniforms(Shader_Tile);
385   glUniform1i(uniformLocations.render_stage, stage);
386   glUniform1f(uniformLocations.gameTime, gameTime);
387 
388   // Link in texture atlas :-)
389   glUniform1i(uniformLocations.arrtex, 0);
390   glActiveTexture(GL_TEXTURE0 + 0);
391   glBindTexture(GL_TEXTURE_2D_ARRAY, texture_Array);
392 
393   // Run through ye olde draw loop
394   // WALLS
395   for (int i = 0; i < nchunks; i++) {
396     glBindVertexArray(drawlist[i]->wall_vao);
397     glDrawElements(GL_TRIANGLES, CHUNKSIZE * CHUNKSIZE * 12, GL_UNSIGNED_SHORT, (void*)0);
398   }
399 
400   // TOPS
401   for (int i = 0; i < nchunks; i++) {
402     glBindVertexArray(drawlist[i]->tile_vao);
403     glDrawElements(GL_TRIANGLES, CHUNKSIZE * CHUNKSIZE * 12, GL_UNSIGNED_SHORT, (void*)0);
404   }
405 
406   if (stage == 1 && activeView.show_flag_state) {
407     glActiveTexture(GL_TEXTURE0 + 0);
408     glBindTexture(GL_TEXTURE_2D_ARRAY, flag_Array);
409 
410     for (int i = 0; i < nchunks; i++) {
411       glBindVertexArray(drawlist[i]->flag_vao);
412       glDrawElements(GL_TRIANGLES, CHUNKSIZE * CHUNKSIZE * 12, GL_UNSIGNED_SHORT, (void*)0);
413     }
414   }
415 
416   if (stage == 1 && !activeView.calculating_shadows) {
417     setActiveProgramAndUniforms(Shader_Water);
418     glUniform1f(uniformLocations.gameTime, gameTime);
419     glUniform1i(uniformLocations.wtex, 0);
420     glActiveTexture(GL_TEXTURE0 + 0);
421     glBindTexture(GL_TEXTURE_2D, textures[tx_Water]);
422 
423     // Water
424     for (int i = 0; i < nchunks; i++) {
425       glBindVertexArray(drawlist[i]->flui_vao);
426       glDrawElements(GL_TRIANGLES, CHUNKSIZE * CHUNKSIZE * 12, GL_UNSIGNED_SHORT, (void*)0);
427     }
428   }
429 
430   if (!activeView.calculating_shadows) {
431     setActiveProgramAndUniforms(Shader_Line);
432     glUniformC(uniformLocations.line_color, Color(0., 0., 0., 1.));
433 
434     glEnable(GL_LINE_SMOOTH);
435     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
436     glLineWidth(2.0f);
437     for (int i = 0; i < nchunks; i++) {
438       glBindVertexArray(drawlist[i]->line_vao);
439       glDrawElements(GL_LINES, CHUNKSIZE * CHUNKSIZE * 8, GL_UNSIGNED_SHORT, (void*)0);
440     }
441     glDisable(GL_LINE_SMOOTH);
442   }
443 
444   // Don't keep any VAOs bound/overwritable
445   glBindVertexArray(0);
446   warnForGLerrors(stage ? "Map drawing 1" : "Map drawing 0");
447 }
448 
packCell(GLfloat * fout,GLfloat px,GLfloat py,GLfloat pz,Color color,GLfloat tx,GLfloat ty,int txno,float velx,float vely,const GLfloat normal[3])449 static inline void packCell(GLfloat* fout, GLfloat px, GLfloat py, GLfloat pz, Color color,
450                             GLfloat tx, GLfloat ty, int txno, float velx, float vely,
451                             const GLfloat normal[3]) {
452   uint32_t* aout = (uint32_t*)fout;
453   fout[0] = px;
454   fout[1] = py;
455   fout[2] = pz;
456   aout[3] = ((uint32_t)(color.v[1]) << 16) + (uint32_t)(color.v[0]);
457   aout[4] = ((uint32_t)(color.v[3]) << 16) + (uint32_t)(color.v[2]);
458 
459   uint32_t txco = ((uint32_t)(1023.f * ty) << 10) | ((uint32_t)(1023.f * tx) << 0) |
460                   ((uint32_t)(1023.f * (txno / 16.f)) << 20);
461   aout[5] = txco;
462   aout[6] = packNormal(normal);
463 
464   int vx = std::max(std::min((int)(32677.f * (velx)), 32677), -32677);
465   int vy = std::max(std::min((int)(32677.f * (vely)), 32677), -32677);
466   aout[7] = ((uint32_t)vy << 16) + (uint32_t)vx;
467 }
packWaterCell(GLfloat * fout,GLfloat px,GLfloat py,GLfloat pz,const float vel[2],GLfloat tx,GLfloat ty,const GLfloat normal[3])468 static inline void packWaterCell(GLfloat* fout, GLfloat px, GLfloat py, GLfloat pz,
469                                  const float vel[2], GLfloat tx, GLfloat ty,
470                                  const GLfloat normal[3]) {
471   uint32_t* aout = (uint32_t*)fout;
472   fout[0] = px;
473   fout[1] = py;
474   fout[2] = pz;
475   aout[3] = -1;
476   aout[4] = -1;
477   aout[5] = (((uint32_t)(65535.f * ty)) << 16) + (uint32_t)(65535.f * tx);
478   aout[6] = packNormal(normal);
479   int vx = std::max(std::min((int)(32677.f * (0.25f * vel[0])), 32677), -32677);
480   int vy = std::max(std::min((int)(32677.f * (0.25f * vel[1])), 32677), -32677);
481   aout[7] = ((uint32_t)vy << 16) + (uint32_t)vx;
482 }
483 
cmp(float l,float r)484 static inline int cmp(float l, float r) {
485   if (l > r) return 1;
486   if (r > l) return -1;
487   return 0;
488 }
489 
fillChunkVBO(Chunk * chunk) const490 void Map::fillChunkVBO(Chunk* chunk) const {
491   GLfloat* tdat = new GLfloat[CHUNKSIZE * CHUNKSIZE * 5 * 8];
492   ushort* tidx = new ushort[CHUNKSIZE * CHUNKSIZE * 12];
493 
494   GLfloat* sdat = NULL;
495   ushort* sidx = NULL;
496   if (activeView.show_flag_state) {
497     sdat = new GLfloat[CHUNKSIZE * CHUNKSIZE * 5 * 8];
498     sidx = new ushort[CHUNKSIZE * CHUNKSIZE * 12];
499   }
500   GLfloat* wdat = new GLfloat[CHUNKSIZE * CHUNKSIZE * 8 * 8];
501   ushort* widx = new ushort[CHUNKSIZE * CHUNKSIZE * 12];
502   GLfloat* ldat = new GLfloat[CHUNKSIZE * CHUNKSIZE * 4 * 3];
503   ushort* lidx = new ushort[CHUNKSIZE * CHUNKSIZE * 8];
504   GLfloat* fdat = new GLfloat[CHUNKSIZE * CHUNKSIZE * 8 * 5];
505   ushort* fidx = new ushort[CHUNKSIZE * CHUNKSIZE * 12];
506 
507   // Create data if not already there
508   bool first_time = false;
509   if (!chunk->is_active) {
510     glGenBuffers(2, &chunk->tile_vbo[0]);
511     glGenBuffers(2, &chunk->flag_vbo[0]);
512     glGenBuffers(2, &chunk->wall_vbo[0]);
513     glGenBuffers(2, &chunk->line_vbo[0]);
514     glGenBuffers(2, &chunk->flui_vbo[0]);
515     glGenVertexArrays(1, &chunk->tile_vao);
516     glGenVertexArrays(1, &chunk->flag_vao);
517     glGenVertexArrays(1, &chunk->wall_vao);
518     glGenVertexArrays(1, &chunk->line_vao);
519     glGenVertexArrays(1, &chunk->flui_vao);
520     first_time = true;
521   }
522   chunk->is_active = true;
523 
524   // Update exact chunk bounds as well, and mark all first-timers as
525   // updated
526   GLfloat minz = 1e99, maxz = -1e99;
527   for (int x = chunk->xm; x < chunk->xm + CHUNKSIZE; x++) {
528     for (int y = chunk->ym; y < chunk->ym + CHUNKSIZE; y++) {
529       Cell& c = cell(x, y);
530       c.displayListDirty |= first_time;
531       for (int k = 0; k < 5; k++) {
532         minz = std::min(minz, c.heights[k]);
533         minz = std::min(minz, c.waterHeights[k]);
534         maxz = std::max(maxz, c.heights[k]);
535         maxz = std::max(maxz, c.waterHeights[k]);
536       }
537     }
538   }
539   int jstart = -1;
540   for (int j = 0; j < CHUNKSIZE * CHUNKSIZE; j++) {
541     int x = chunk->xm + j % CHUNKSIZE;
542     int y = chunk->ym + j / CHUNKSIZE;
543     Cell& c = cell(x, y);
544     if (c.displayListDirty) {
545       if (jstart < 0) { jstart = j; }
546 
547       int txno = 0;
548       int typed = c.flags & (CELL_ICE | CELL_ACID | CELL_TRACK | CELL_SAND);
549       if (typed || c.texture >= 0) {
550         if ((c.flags & CELL_ICE) || (!typed && c.texture == tx_Ice)) {
551           txno = 1;
552         } else if ((c.flags & CELL_SAND) || (!typed && c.texture == tx_Sand)) {
553           txno = 2;
554         } else if ((c.flags & CELL_TRACK) || (!typed && c.texture == tx_Track)) {
555           txno = 4;
556         } else if ((c.flags & CELL_ACID) || (!typed && c.texture == tx_Acid)) {
557           txno = 3;
558         } else if (c.texture == tx_1) {
559           txno = 5;
560         } else if (c.texture == tx_2) {
561           txno = 6;
562         } else if (c.texture == tx_3) {
563           txno = 7;
564         } else if (c.texture == tx_4) {
565           txno = 8;
566         }
567       } else {
568         // Nothing much...
569       }
570 
571       GLfloat nnormal[3] = {0.f, 1.f, 0.f};
572       GLfloat snormal[3] = {0.f, -1.f, 0.f};
573       GLfloat enormal[3] = {-1.f, 0.f, 0.f};
574       GLfloat wnormal[3] = {1.f, 0.f, 0.f};
575 
576       float fcnormal[5][3];
577       if (c.flags & CELL_SHADE_FLAT) {
578         for (size_t i = 0; i < 15; i++) { fcnormal[i / 3][i % 3] = 0.f; }
579       } else {
580         Coord3d cnormal[5];
581         c.getNormals(cnormal);
582         for (size_t i = 0; i < 15; i++) {
583           fcnormal[i / 3][i % 3] = (float)cnormal[i / 3][i % 3];
584         }
585       }
586 
587       int k = j * 5 * 8;
588       const int texscale = 3.;
589       const float irs = 1.f / texscale;
590       // ((. % t)+t)%t handles negative numbers
591       GLfloat tx = (((x % texscale) + texscale) % texscale) * irs;
592       GLfloat ty = (((y % texscale) + texscale) % texscale) * irs;
593       if (tx >= 1.) tx = 0.;
594       if (ty >= 1.) ty = 0.;
595       packCell(&tdat[k], x, y, c.heights[Cell::SW], c.colors[Cell::SW], tx, ty, txno,
596                c.velocity[0] * irs, c.velocity[1] * irs, fcnormal[Cell::SW]);
597       packCell(&tdat[k + 8], x + 1, y, c.heights[Cell::SE], c.colors[Cell::SE], tx + irs, ty,
598                txno, c.velocity[0] * irs, c.velocity[1] * irs, fcnormal[Cell::SE]);
599       packCell(&tdat[k + 16], x + 1, y + 1, c.heights[Cell::NE], c.colors[Cell::NE], tx + irs,
600                ty + irs, txno, c.velocity[0] * irs, c.velocity[1] * irs, fcnormal[Cell::NE]);
601       packCell(&tdat[k + 24], x, y + 1, c.heights[Cell::NW], c.colors[Cell::NW], tx, ty + irs,
602                txno, c.velocity[0] * irs, c.velocity[1] * irs, fcnormal[Cell::NW]);
603       packCell(&tdat[k + 32], x + 0.5f, y + 0.5f, c.heights[Cell::CENTER],
604                c.colors[Cell::CENTER], tx + irs / 2, ty + irs / 2, txno, c.velocity[0] * irs,
605                c.velocity[1] * irs, fcnormal[Cell::CENTER]);
606 
607       const ushort topindices[12] = {0, 1, 4, 1, 2, 4, 2, 3, 4, 3, 0, 4};
608       for (uint i = 0; i < 12; i++) { tidx[j * 12 + i] = 5 * j + topindices[i]; }
609 
610       if (activeView.show_flag_state) {
611         Color white(1.f, 1.f, 1.f, 1.f);
612         float height_boost = 0.001f;
613 
614         int flag_list[6] = {CELL_ICE,  CELL_SAND,       CELL_TRACK,
615                             CELL_ACID, CELL_TRAMPOLINE, CELL_KILL};
616 
617         int flagno = 0;
618         int nflags = 0;
619         for (int k = 0; k < 6; k++) {
620           if (c.flags & flag_list[k]) {
621             nflags++;
622             flagno = k + 2;
623           }
624         }
625         if (nflags > 1) { flagno = 1; }
626 
627         packCell(&sdat[k], x, y, c.heights[Cell::SW] + height_boost, white, 0.f, 0.f, flagno,
628                  0.f, 0.f, fcnormal[Cell::SW]);
629         packCell(&sdat[k + 8], x + 1, y, c.heights[Cell::SE] + height_boost, white, 1.f, 0.f,
630                  flagno, 0.f, 0.f, fcnormal[Cell::SE]);
631         packCell(&sdat[k + 16], x + 1, y + 1, c.heights[Cell::NE] + height_boost, white, 1.f,
632                  1.f, flagno, 0.f, 0.f, fcnormal[Cell::NE]);
633         packCell(&sdat[k + 24], x, y + 1, c.heights[Cell::NW] + height_boost, white, 0.f, 1.f,
634                  flagno, 0.f, 0.f, fcnormal[Cell::NW]);
635         packCell(&sdat[k + 32], x + 0.5f, y + 0.5f, c.heights[Cell::CENTER] + height_boost,
636                  white, 0.5f, 0.5f, flagno, 0.f, 0.f, fcnormal[Cell::CENTER]);
637 
638         for (uint i = 0; i < 12; i++) { sidx[j * 12 + i] = 5 * j + topindices[i]; }
639       }
640 
641       // We assume that a quad suffices to render each wall
642       // (otherwise, in the worst case, we'd need 6 vertices/side!
643       int p = j * 8 * 8;
644       const Cell& ns = cell(x, y - 1);
645       const float nv[2] = {0.f, 0.f};
646       int nsover = cmp(c.heights[Cell::SW], ns.heights[Cell::NW]) +
647                        cmp(c.heights[Cell::SE], ns.heights[Cell::NE]) >
648                    0;
649       packCell(&wdat[p], x, y, c.heights[Cell::SW], c.wallColors[Cell::SW], 0.5f, 0.5f, 0,
650                nv[0], nv[1], nsover ? snormal : nnormal);
651       packCell(&wdat[p + 8], x, y, ns.heights[Cell::NW], ns.wallColors[Cell::NW], 0.5f, 0.5f,
652                0, nv[0], nv[1], nsover ? snormal : nnormal);
653       packCell(&wdat[p + 16], x + 1, y, c.heights[Cell::SE], c.wallColors[Cell::SE], 0.5f,
654                0.5f, 0, nv[0], nv[1], nsover ? snormal : nnormal);
655       packCell(&wdat[p + 24], x + 1, y, ns.heights[Cell::NE], ns.wallColors[Cell::NE], 0.5f,
656                0.5f, 0, nv[0], nv[1], nsover ? snormal : nnormal);
657 
658       const Cell& ne = cell(x - 1, y);
659       int ewover = cmp(c.heights[Cell::SW], ne.heights[Cell::SE]) +
660                        cmp(c.heights[Cell::NW], ne.heights[Cell::NE]) >
661                    0;
662       packCell(&wdat[p + 32], x, y, c.heights[Cell::SW], c.wallColors[Cell::SW], 0.5f, 0.5f, 0,
663                nv[0], nv[1], ewover ? enormal : wnormal);
664       packCell(&wdat[p + 40], x, y, ne.heights[Cell::SE], ne.wallColors[Cell::SE], 0.5f, 0.5f,
665                0, nv[0], nv[1], ewover ? enormal : wnormal);
666       packCell(&wdat[p + 48], x, y + 1, c.heights[Cell::NW], c.wallColors[Cell::NW], 0.5f,
667                0.5f, 0, nv[0], nv[1], ewover ? enormal : wnormal);
668       packCell(&wdat[p + 56], x, y + 1, ne.heights[Cell::NE], ne.wallColors[Cell::NE], 0.5f,
669                0.5f, 0, nv[0], nv[1], ewover ? enormal : wnormal);
670 
671       // Render the quads, overlapping triangles if twisted
672       const ushort quadmap[6] = {0, 1, 2, 1, 3, 2};
673       const ushort utriang[6] = {0, 1, 3, 0, 3, 2};
674       const ushort dtriang[6] = {0, 1, 2, 1, 2, 3};
675       const ushort* sel;
676       if (cmp(c.heights[Cell::SW], ns.heights[Cell::NW]) *
677               cmp(c.heights[Cell::SE], ns.heights[Cell::NE]) >=
678           0) {
679         sel = quadmap;
680       } else if (c.heights[Cell::SW] < ns.heights[Cell::NW]) {
681         sel = utriang;
682       } else {
683         sel = dtriang;
684       }
685       for (uint i = 0; i < 6; i++) widx[j * 12 + i] = j * 8 + sel[i];
686 
687       const ushort aquadmap[6] = {0, 2, 1, 2, 3, 1};
688       const ushort autriang[6] = {0, 2, 1, 2, 3, 1};
689       const ushort adtriang[6] = {0, 3, 1, 2, 3, 0};
690       if (cmp(c.heights[Cell::SW], ne.heights[Cell::SE]) +
691           cmp(c.heights[Cell::NW], ne.heights[Cell::NE])) {
692         sel = aquadmap;
693       } else if (c.heights[Cell::SW] > ne.heights[Cell::SE]) {
694         sel = autriang;
695       } else {
696         sel = adtriang;
697       }
698       for (uint i = 0; i < 6; i++) widx[j * 12 + 6 + i] = j * 8 + 4 + sel[i];
699 
700       // Line data ! (thankfully they're all black)
701       int s = j * 12;
702       ldat[s++] = float(x);
703       ldat[s++] = float(y);
704       ldat[s++] = c.heights[Cell::SW];
705       ldat[s++] = float(x + 1);
706       ldat[s++] = float(y);
707       ldat[s++] = c.heights[Cell::SE];
708       ldat[s++] = float(x + 1);
709       ldat[s++] = float(y + 1);
710       ldat[s++] = c.heights[Cell::NE];
711       ldat[s++] = float(x);
712       ldat[s++] = float(y + 1);
713       ldat[s++] = c.heights[Cell::NW];
714 
715       int t = j * 8;
716       // We disable lines by doubling indices
717       lidx[t++] = 4 * j + 0;
718       lidx[t++] = 4 * j + (c.flags & (CELL_NOLINESOUTH | CELL_NOGRID) ? 0 : 1);
719       lidx[t++] = 4 * j + 1;
720       lidx[t++] = 4 * j + (c.flags & (CELL_NOLINEEAST | CELL_NOGRID) ? 1 : 2);
721       lidx[t++] = 4 * j + 2;
722       lidx[t++] = 4 * j + (c.flags & (CELL_NOLINENORTH | CELL_NOGRID) ? 2 : 3);
723       lidx[t++] = 4 * j + 3;
724       lidx[t++] = 4 * j + (c.flags & (CELL_NOLINEWEST | CELL_NOGRID) ? 3 : 0);
725 
726       // Water
727       int wvis = c.isWaterVisible();
728       if (wvis) {
729         Coord3d onormal[5];
730         c.getWaterNormals(onormal);
731         float fonormal[5][3];
732         for (size_t i = 0; i < 15; i++) {
733           fonormal[i / 3][i % 3] = (float)onormal[i / 3][i % 3];
734         }
735 
736         int u = j * 8 * 5;
737         packWaterCell(&fdat[u], x, y, c.waterHeights[Cell::SW], c.velocity, 0.0f, 0.0f,
738                       fonormal[Cell::SW]);
739         packWaterCell(&fdat[u + 8], x + 1, y, c.waterHeights[Cell::SE], c.velocity, 1.0f, 0.0f,
740                       fonormal[Cell::SE]);
741         packWaterCell(&fdat[u + 16], x + 1, y + 1, c.waterHeights[Cell::NE], c.velocity, 1.0f,
742                       1.0f, fonormal[Cell::NE]);
743         packWaterCell(&fdat[u + 24], x, y + 1, c.waterHeights[Cell::NW], c.velocity, 0.0f,
744                       1.0f, fonormal[Cell::NW]);
745         packWaterCell(&fdat[u + 32], x + 0.5f, y + 0.5f, c.waterHeights[Cell::CENTER],
746                       c.velocity, 0.5f, 0.5f, fonormal[Cell::CENTER]);
747 
748         for (uint i = 0; i < 12; i++) { fidx[j * 12 + i] = 5 * j + topindices[i]; }
749       } else {
750         // Zero tile (safer than uninitialized)
751         memset(&fdat[j * 5 * 8], 0, 5 * 8 * sizeof(GLfloat));
752         for (int i = 0; i < 12; i++) { fidx[j * 12 + i] = 0; }
753       }
754     }
755     if ((!c.displayListDirty || j == CHUNKSIZE * CHUNKSIZE - 1) && jstart >= 0) {
756       int jend = c.displayListDirty ? CHUNKSIZE * CHUNKSIZE : j;
757       size_t tile_data_size = 5 * 8 * sizeof(GLfloat);
758       size_t tile_index_size = 12 * sizeof(ushort);
759       size_t wall_data_size = 8 * 8 * sizeof(GLfloat);
760       size_t wall_index_size = 12 * sizeof(ushort);
761       size_t line_data_size = 4 * 3 * sizeof(GLfloat);
762       size_t line_index_size = 8 * sizeof(ushort);
763       size_t flui_data_size = 5 * 8 * sizeof(GLfloat);
764       size_t flui_index_size = 12 * sizeof(ushort);
765 
766       if (first_time) {
767         // Tiles
768         glBindVertexArray(chunk->tile_vao);
769         glBindBuffer(GL_ARRAY_BUFFER, chunk->tile_vbo[0]);
770         glBufferData(GL_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * tile_data_size, tdat,
771                      GL_DYNAMIC_DRAW);
772         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->tile_vbo[1]);
773         glBufferData(GL_ELEMENT_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * tile_index_size, tidx,
774                      GL_DYNAMIC_DRAW);
775         configureCellAttributes(false);
776 
777         if (activeView.show_flag_state) {
778           glBindVertexArray(chunk->flag_vao);
779           glBindBuffer(GL_ARRAY_BUFFER, chunk->flag_vbo[0]);
780           glBufferData(GL_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * tile_data_size, sdat,
781                        GL_DYNAMIC_DRAW);
782           glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->flag_vbo[1]);
783           glBufferData(GL_ELEMENT_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * tile_index_size, sidx,
784                        GL_DYNAMIC_DRAW);
785           configureCellAttributes(false);
786         }
787 
788         // Walls
789         glBindVertexArray(chunk->wall_vao);
790         glBindBuffer(GL_ARRAY_BUFFER, chunk->wall_vbo[0]);
791         glBufferData(GL_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * wall_data_size, wdat,
792                      GL_DYNAMIC_DRAW);
793         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->wall_vbo[1]);
794         glBufferData(GL_ELEMENT_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * wall_index_size, widx,
795                      GL_DYNAMIC_DRAW);
796         configureCellAttributes(false);
797         // Lines
798         glBindVertexArray(chunk->line_vao);
799         glBindBuffer(GL_ARRAY_BUFFER, chunk->line_vbo[0]);
800         glBufferData(GL_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * line_data_size, ldat,
801                      GL_DYNAMIC_DRAW);
802         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->line_vbo[1]);
803         glBufferData(GL_ELEMENT_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * line_index_size, lidx,
804                      GL_DYNAMIC_DRAW);
805         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
806         glEnableVertexAttribArray(0);
807         // Water
808         glBindVertexArray(chunk->flui_vao);
809         glBindBuffer(GL_ARRAY_BUFFER, chunk->flui_vbo[0]);
810         glBufferData(GL_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * flui_data_size, fdat,
811                      GL_DYNAMIC_DRAW);
812         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->flui_vbo[1]);
813         glBufferData(GL_ELEMENT_ARRAY_BUFFER, CHUNKSIZE * CHUNKSIZE * flui_index_size, fidx,
814                      GL_DYNAMIC_DRAW);
815         configureCellAttributes(true);
816       } else {
817         // chunk VAOs will automatically use their previously bound VBOs
818         glBindVertexArray(0);
819 
820         // Tiles
821         glBindBuffer(GL_ARRAY_BUFFER, chunk->tile_vbo[0]);
822         glBufferSubData(GL_ARRAY_BUFFER, jstart * tile_data_size,
823                         (jend - jstart) * tile_data_size,
824                         &tdat[jstart * tile_data_size / sizeof(GLfloat)]);
825         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->tile_vbo[1]);
826         glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, jstart * tile_index_size,
827                         (jend - jstart) * tile_index_size,
828                         &tidx[jstart * tile_index_size / sizeof(ushort)]);
829 
830         if (activeView.show_flag_state) {
831           glBindBuffer(GL_ARRAY_BUFFER, chunk->flag_vbo[0]);
832           glBufferSubData(GL_ARRAY_BUFFER, jstart * tile_data_size,
833                           (jend - jstart) * tile_data_size,
834                           &sdat[jstart * tile_data_size / sizeof(GLfloat)]);
835           glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->tile_vbo[1]);
836           glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, jstart * tile_index_size,
837                           (jend - jstart) * tile_index_size,
838                           &sidx[jstart * tile_index_size / sizeof(ushort)]);
839         }
840 
841         // Walls
842         glBindBuffer(GL_ARRAY_BUFFER, chunk->wall_vbo[0]);
843         glBufferSubData(GL_ARRAY_BUFFER, jstart * wall_data_size,
844                         (jend - jstart) * wall_data_size,
845                         &wdat[jstart * wall_data_size / sizeof(GLfloat)]);
846         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->wall_vbo[1]);
847         glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, jstart * wall_index_size,
848                         (jend - jstart) * wall_index_size,
849                         &widx[jstart * wall_index_size / sizeof(ushort)]);
850         // Lines
851         glBindBuffer(GL_ARRAY_BUFFER, chunk->line_vbo[0]);
852         glBufferSubData(GL_ARRAY_BUFFER, jstart * line_data_size,
853                         (jend - jstart) * line_data_size,
854                         &ldat[jstart * line_data_size / sizeof(GLfloat)]);
855         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->line_vbo[1]);
856         glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, jstart * line_index_size,
857                         (jend - jstart) * line_index_size,
858                         &lidx[jstart * line_index_size / sizeof(ushort)]);
859         // Water
860         glBindBuffer(GL_ARRAY_BUFFER, chunk->flui_vbo[0]);
861         glBufferSubData(GL_ARRAY_BUFFER, jstart * flui_data_size,
862                         (jend - jstart) * flui_data_size,
863                         &fdat[jstart * flui_data_size / sizeof(GLfloat)]);
864         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunk->flui_vbo[1]);
865         glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, jstart * flui_index_size,
866                         (jend - jstart) * flui_index_size,
867                         &tidx[jstart * flui_index_size / sizeof(ushort)]);
868       }
869       jstart = -1;
870     }
871   }
872 
873   // Wipe dirty list. This _MUST_ be a separate phase
874   // since cell(x,y) is not guaranteed to be unique!
875   for (int x = chunk->xm; x < chunk->xm + CHUNKSIZE; x++) {
876     for (int y = chunk->ym; y < chunk->ym + CHUNKSIZE; y++) {
877       cell(x, y).displayListDirty = false;
878     }
879   }
880 
881   // Tiles
882 
883   delete[] tdat;
884   delete[] tidx;
885   delete[] wdat;
886   delete[] widx;
887   delete[] ldat;
888   delete[] lidx;
889   delete[] fdat;
890   delete[] fidx;
891 
892   // Update view parameters
893   chunk->minHeight = minz;
894   chunk->maxHeight = maxz;
895   chunk->is_updated = false;
896 }
897 
markCellsUpdated(int x1,int y1,int x2,int y2,bool changed_walls)898 void Map::markCellsUpdated(int x1, int y1, int x2, int y2, bool changed_walls) {
899   /* If the walls of a cell have changed, update its neighbors */
900   int dd = changed_walls ? 1 : 0;
901 
902   /* Apply the same clamping to bounds as for cell lookups */
903   int xmin = std::max(std::min(std::min(x1, x2) - dd, width - 1), 0);
904   int xmax = std::max(std::min(std::max(x1, x2) + dd, width - 1), 0);
905   int ymin = std::max(std::min(std::min(y1, y2) - dd, height - 1), 0);
906   int ymax = std::max(std::min(std::max(y1, y2) + dd, height - 1), 0);
907 
908   for (int xp = xmin - xmin % CHUNKSIZE; xp <= xmax - xmax % CHUNKSIZE; xp += CHUNKSIZE) {
909     for (int yp = ymin - ymin % CHUNKSIZE; yp <= ymax - ymax % CHUNKSIZE; yp += CHUNKSIZE) {
910       Chunk* z = chunk(xp, yp);
911       z->is_updated = true;
912       for (int x = std::max(xmin, xp); x <= std::min(xmax, xp + CHUNKSIZE - 1); x++) {
913         for (int y = std::max(ymin, yp); y <= std::min(ymax, yp + CHUNKSIZE - 1); y++) {
914           Cell& c = cells[x + width * y];
915           c.displayListDirty = true;
916           for (int k = 0; k < 5; k++) {
917             z->minHeight = std::min(z->minHeight, std::min(c.heights[k], c.waterHeights[k]));
918             z->maxHeight = std::max(z->maxHeight, std::max(c.heights[k], c.waterHeights[k]));
919           }
920         }
921       }
922     }
923   }
924 }
925 
drawFootprint(int x1,int y1,int x2,int y2,int kind)926 void Map::drawFootprint(int x1, int y1, int x2, int y2, int kind) {
927   Color color(kind ? 0.5 : 0.2, 0.2, kind ? 0.2 : 0.5, 1.0);
928 
929   int ncells = (std::abs(x1 - x2) + 1) * (std::abs(y1 - y2) + 1);
930   if (ncells > 2 * 256 * 256) {
931     warning("Footprint requested for too large an area. Drawing nothing.");
932     glEnable(GL_DEPTH_TEST);
933     return;
934   }
935 
936   GLfloat* data = new GLfloat[8 * 8 * ncells];
937   uint* idxs = new uint[8 * 3 * ncells];
938 
939   GLfloat edge = 0.05f;
940   const GLfloat flat[3] = {0.f, 0.f, 0.f};
941 
942   int j = 0;
943   for (int x = std::min(x1, x2); x <= std::max(x1, x2); ++x) {
944     for (int y = std::min(y1, y2); y <= std::max(y1, y2); ++y) {
945       Cell& center = cell(x, y);
946       packObjectVertex(&data[j * 8 * 8], x + edge, y + edge, center.heights[Cell::SW], 0., 0.,
947                        color, flat);
948       packObjectVertex(&data[j * 8 * 8 + 8], x + 1 - edge, y + edge, center.heights[Cell::SE],
949                        0., 0., color, flat);
950       packObjectVertex(&data[j * 8 * 8 + 16], x + 1 - edge, y + 1 - edge,
951                        center.heights[Cell::NE], 0., 0., color, flat);
952       packObjectVertex(&data[j * 8 * 8 + 24], x + edge, y + 1 - edge, center.heights[Cell::NW],
953                        0., 0., color, flat);
954       packObjectVertex(&data[j * 8 * 8 + 32], x - edge, y - edge, center.heights[Cell::SW], 0.,
955                        0., color, flat);
956       packObjectVertex(&data[j * 8 * 8 + 40], x + 1 + edge, y - edge, center.heights[Cell::SE],
957                        0., 0., color, flat);
958       packObjectVertex(&data[j * 8 * 8 + 48], x + 1 + edge, y + 1 + edge,
959                        center.heights[Cell::NE], 0., 0., color, flat);
960       packObjectVertex(&data[j * 8 * 8 + 56], x - edge, y + 1 + edge, center.heights[Cell::NW],
961                        0., 0., color, flat);
962 
963       uint local_triangles[8][3] = {{4, 5, 0}, {0, 5, 1}, {6, 1, 5}, {1, 6, 2},
964                                     {7, 2, 6}, {2, 7, 3}, {4, 3, 7}, {3, 4, 0}};
965       for (int i = 0; i < 24; i++) {
966         idxs[j * 24 + i] = local_triangles[i / 3][i % 3] + 8 * j;
967       }
968       j++;
969     }
970   }
971 
972   GLuint databuf, idxbuf, vao;
973   glGenBuffers(1, &databuf);
974   glGenBuffers(1, &idxbuf);
975   glGenVertexArrays(1, &vao);
976 
977   glBindVertexArray(vao);
978   glBindBuffer(GL_ARRAY_BUFFER, databuf);
979   glBufferData(GL_ARRAY_BUFFER, 8 * 8 * ncells * sizeof(GLfloat), data, GL_STATIC_DRAW);
980 
981   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxbuf);
982   glBufferData(GL_ELEMENT_ARRAY_BUFFER, 8 * 3 * ncells * sizeof(uint), idxs, GL_STATIC_DRAW);
983   delete[] data;
984   delete[] idxs;
985 
986   glDisable(GL_DEPTH_TEST);
987   glEnable(GL_BLEND);
988   glDisable(GL_CULL_FACE);
989 
990   setActiveProgramAndUniforms(Shader_Object);
991   setObjectUniforms(Color(0., 0., 0., 1.), 0., Lighting_None);
992 
993   glBindTexture(GL_TEXTURE_2D, textureBlank);
994 
995   configureObjectAttributes();
996   glDrawElements(GL_TRIANGLES, 8 * 3 * ncells, GL_UNSIGNED_INT, (void*)0);
997 
998   glBindVertexArray(0);
999   glDeleteBuffers(1, &databuf);
1000   glDeleteBuffers(1, &idxbuf);
1001   glDeleteVertexArrays(1, &vao);
1002 
1003   glEnable(GL_DEPTH_TEST);
1004 }
1005 
drawLoop(int x1,int y1,int x2,int y2,int kind)1006 void Map::drawLoop(int x1, int y1, int x2, int y2, int kind) {
1007   Color color(0.2, kind ? 0.2 : 0.8, 0.2, 1.0);
1008 
1009   int npts = 4 * std::abs(x1 - x2) + 4 * std::abs(y1 - y2) + 8;
1010   Coord3d* ringPoints = new Coord3d[npts + 2];
1011   Coord3d* rp = ringPoints;
1012   int k = 1;
1013   /* Traverse loop in order. SW is x,y; N is y+1, E is x+1. */
1014   int xl = std::min(x1, x2), xh = std::max(x1, x2), yl = std::min(y1, y2),
1015       yh = std::max(y1, y2);
1016   for (int x = xl; x <= xh; x++) {
1017     Cell& c = cell(x, yl);
1018     Coord3d lx((double)x, (double)yl, c.heights[Cell::SW]);
1019     Coord3d ly((double)x + 1, (double)yl, c.heights[Cell::SE]);
1020     rp[k] = lx;
1021     k++;
1022     rp[k] = ly;
1023     k++;
1024   }
1025   for (int y = yl; y <= yh; y++) {
1026     Cell& c = cell(xh, y);
1027     Coord3d lx((double)xh + 1, (double)y, c.heights[Cell::SE]);
1028     Coord3d ly((double)xh + 1, (double)y + 1, c.heights[Cell::NE]);
1029     rp[k] = lx;
1030     k++;
1031     rp[k] = ly;
1032     k++;
1033   }
1034   for (int x = xh; x >= xl; x--) {
1035     Cell& c = cell(x, yh);
1036     Coord3d lx((double)x + 1, (double)yh + 1, c.heights[Cell::NE]);
1037     Coord3d ly((double)x, (double)yh + 1, c.heights[Cell::NW]);
1038     rp[k] = lx;
1039     k++;
1040     rp[k] = ly;
1041     k++;
1042   }
1043   for (int y = yh; y >= yl; y--) {
1044     Cell& c = cell(xl, y);
1045     Coord3d lx((double)xl, (double)y + 1, c.heights[Cell::NW]);
1046     Coord3d ly((double)xl, (double)y, c.heights[Cell::NE]);
1047     rp[k] = lx;
1048     k++;
1049     rp[k] = ly;
1050     k++;
1051   }
1052   /* Close the loop */
1053   ringPoints[0] = ringPoints[k - 1];
1054   ringPoints[k] = ringPoints[1];
1055 
1056   GLfloat* data = new GLfloat[8 * 2 * npts];
1057   GLfloat width = 0.05f;
1058   GLfloat flat[3] = {0., 0., 0.};
1059   char* pos = (char*)data;
1060   int nontrivial = 0;
1061   for (int i = 1; i < npts + 1; i++) {
1062     Coord3d prev = ringPoints[i - 1], cur = ringPoints[i], nxt = ringPoints[i + 1];
1063     Coord3d dir1 = nxt - cur, dir2 = cur - prev;
1064     if (length(dir1) < 1e-2 && length(dir1) < 1e-2) { continue; }
1065     if (length(dir1) < 1e-2) { dir1 = dir2; }
1066     if (length(dir2) < 1e-2) { dir2 = dir1; }
1067     dir1 = dir1 / length(dir1);
1068     dir2 = dir2 / length(dir2);
1069     Coord3d dir = dir1 + dir2;
1070     if (length(dir) < 1e-2) { continue; }
1071     nontrivial++;
1072     dir = dir / length(dir);
1073     dir[2] = 0;
1074     Coord3d up(0., 0., 1.);
1075     Coord3d res = crossProduct(up, dir);
1076     res[2] = 0.;
1077     if (length(res) > 0) res = res / length(res);
1078     Coord3d anti = crossProduct(dir, res);
1079     GLfloat off = 1e-2;
1080     pos += packObjectVertex(pos, cur[0] + width * res[0] + off * anti[0],
1081                             cur[1] + width * res[1] + off * anti[1], cur[2] + off * anti[2],
1082                             0., 0., color, flat);
1083     pos += packObjectVertex(pos, cur[0] - width * res[0] + off * anti[0],
1084                             cur[1] - width * res[1] + off * anti[1], cur[2] + off * anti[2],
1085                             0., 0., color, flat);
1086   }
1087   ushort* idxs = new ushort[2 * nontrivial * 3];
1088   for (int i = 0; i < nontrivial; i++) {
1089     int j = (i + 1) % nontrivial;
1090     idxs[6 * i + 0] = 2 * j;
1091     idxs[6 * i + 1] = 2 * i;
1092     idxs[6 * i + 2] = 2 * j + 1;
1093     idxs[6 * i + 3] = 2 * i;
1094     idxs[6 * i + 4] = 2 * i + 1;
1095     idxs[6 * i + 5] = 2 * j + 1;
1096   }
1097   delete[] ringPoints;
1098 
1099   // Transfer data
1100   GLuint databuf, idxbuf, vao;
1101   glGenBuffers(1, &databuf);
1102   glGenBuffers(1, &idxbuf);
1103   glGenVertexArrays(1, &vao);
1104 
1105   glBindVertexArray(vao);
1106   glBindBuffer(GL_ARRAY_BUFFER, databuf);
1107   glBufferData(GL_ARRAY_BUFFER, 8 * 2 * nontrivial * sizeof(GLfloat), data, GL_STATIC_DRAW);
1108 
1109   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxbuf);
1110   glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * 3 * nontrivial * sizeof(ushort), idxs,
1111                GL_STATIC_DRAW);
1112   delete[] data;
1113   delete[] idxs;
1114 
1115   glEnable(GL_BLEND);
1116   glEnable(GL_CULL_FACE);
1117 
1118   setActiveProgramAndUniforms(Shader_Object);
1119   setObjectUniforms(Color(0., 0., 0., 1.), 0., Lighting_None);
1120 
1121   glBindTexture(GL_TEXTURE_2D, textureBlank);
1122 
1123   configureObjectAttributes();
1124   glDrawElements(GL_TRIANGLES, 2 * 3 * npts, GL_UNSIGNED_SHORT, (void*)0);
1125 
1126   glBindVertexArray(0);
1127   glDeleteBuffers(1, &databuf);
1128   glDeleteBuffers(1, &idxbuf);
1129   glDeleteVertexArrays(1, &vao);
1130 }
1131 
drawSpotRing(Real x1,Real y1,Real r,int kind)1132 void Map::drawSpotRing(Real x1, Real y1, Real r, int kind) {
1133   int nfacets = std::max(12, (int)(10. * r));
1134   GLfloat width = std::min(0.5 * r, 0.05);
1135   Color color(kind == 0 ? 1. : 0.5, kind == 1 ? 1. : 0.5, kind == 2 ? 1. : 0.5, 1.);
1136   GLfloat flat[3] = {0., 0., 0.};
1137   Real height = Map::getHeight(x1, y1);
1138 
1139   GLfloat* data = new GLfloat[8 * 2 * nfacets];
1140   ushort* idxs = new ushort[3 * 2 * nfacets];
1141   char* pos = (char*)data;
1142 
1143   for (int i = 0; i < nfacets; i++) {
1144     GLfloat angle = 2 * i * M_PI / nfacets;
1145     pos += packObjectVertex(pos, x1 + (r - width) * std::cos(angle),
1146                             y1 + (r - width) * std::sin(angle), height + 1e-2, 0., 0., color,
1147                             flat);
1148     pos += packObjectVertex(pos, x1 + (r + width) * std::cos(angle),
1149                             y1 + (r + width) * std::sin(angle), height + 1e-2, 0., 0., color,
1150                             flat);
1151   }
1152 
1153   for (int i = 0; i < nfacets; i++) {
1154     int j = (i + 1) % nfacets;
1155     idxs[6 * i + 0] = 2 * j;
1156     idxs[6 * i + 1] = 2 * i;
1157     idxs[6 * i + 2] = 2 * j + 1;
1158     idxs[6 * i + 3] = 2 * i;
1159     idxs[6 * i + 4] = 2 * i + 1;
1160     idxs[6 * i + 5] = 2 * j + 1;
1161   }
1162 
1163   // Transfer data
1164   GLuint databuf, idxbuf, vao;
1165   glGenBuffers(1, &databuf);
1166   glGenBuffers(1, &idxbuf);
1167   glGenVertexArrays(1, &vao);
1168 
1169   glBindVertexArray(vao);
1170   glBindBuffer(GL_ARRAY_BUFFER, databuf);
1171   glBufferData(GL_ARRAY_BUFFER, 8 * 2 * nfacets * sizeof(GLfloat), data, GL_STATIC_DRAW);
1172 
1173   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxbuf);
1174   glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * 3 * nfacets * sizeof(ushort), idxs,
1175                GL_STATIC_DRAW);
1176   delete[] data;
1177   delete[] idxs;
1178 
1179   glEnable(GL_BLEND);
1180   glEnable(GL_CULL_FACE);
1181 
1182   setActiveProgramAndUniforms(Shader_Object);
1183   setObjectUniforms(Color(0., 0., 0., 1.), 0., Lighting_None);
1184 
1185   glBindTexture(GL_TEXTURE_2D, textureBlank);
1186   configureObjectAttributes();
1187   glDrawElements(GL_TRIANGLES, 2 * 3 * nfacets, GL_UNSIGNED_SHORT, (void*)0);
1188 
1189   glBindVertexArray(0);
1190   glDeleteBuffers(1, &databuf);
1191   glDeleteBuffers(1, &idxbuf);
1192   glDeleteVertexArrays(1, &vao);
1193 }
1194 
chunk(int cx,int cy) const1195 Chunk* Map::chunk(int cx, int cy) const {
1196   if (cx % CHUNKSIZE != 0 || cy % CHUNKSIZE != 0) {
1197     warning("Bad chunk access %d %d", cx, cy);
1198     return NULL;
1199   }
1200   std::pair<int, int> cpos(cx, cy);
1201   if (chunks.count(cpos) == 0) {
1202     // If local region not present, create associated chunk
1203     // & establish height boundaries
1204     Chunk c = Chunk();
1205     c.is_active = false;
1206     c.is_updated = true;
1207     c.is_visible = false;
1208     c.xm = cx;
1209     c.ym = cy;
1210     c.minHeight = 1e99;
1211     c.maxHeight = 1e-99;
1212     for (int dx = c.xm; dx < c.xm + CHUNKSIZE; dx++) {
1213       for (int dy = c.xm; dy < c.xm + CHUNKSIZE; dy++) {
1214         const Cell& w = cell(dx, dy);
1215         for (int k = 0; k < 5; k++) {
1216           c.maxHeight = std::max(c.maxHeight, std::max(w.heights[k], w.waterHeights[k]));
1217           c.minHeight = std::min(c.minHeight, std::min(w.heights[k], w.waterHeights[k]));
1218         }
1219       }
1220     }
1221     chunks[cpos] = c;
1222   }
1223   return &chunks[cpos];
1224 }
1225 
1226 /** Saves the map to file in compressed binary or compressed ascii format */
save(char * pathname,int x,int y)1227 int Map::save(char* pathname, int x, int y) {
1228   int version = mapFormatVersion;
1229 
1230   if (pathIsLink(pathname)) {
1231     warning("%s is a link, cannot save map", pathname);
1232     return 0;
1233   }
1234 
1235   gzFile gp = gzopen(pathname, "wb9");
1236   if (!gp) return 0;
1237   Coord3d tmp = startPosition;
1238   startPosition = Coord3d(x, y, cell(x, y).heights[Cell::CENTER]);
1239   int32_t data[6];
1240   for (int i = 0; i < 3; i++)
1241     data[i] = saveInt((int32_t)startPosition[i]);  // no decimal part needed
1242   tmp = startPosition;
1243   data[3] = saveInt((int32_t)width);
1244   data[4] = saveInt((int32_t)height);
1245   data[5] = saveInt((int32_t)version);
1246   gzwrite(gp, data, sizeof(int32_t) * 6);
1247 
1248   /* new from version 7, save texture names */
1249   data[0] = saveInt(numTextures);
1250   gzwrite(gp, data, sizeof(int32_t) * 1);
1251   for (int i = 0; i < numTextures; i++) {
1252     char textureName[64];
1253     memset(textureName, 0, sizeof(textureName));
1254     strncpy(textureName, textureNames[i], 63);
1255     gzwrite(gp, textureName, 64);
1256   }
1257 
1258   for (int i = 0; i < width * height; i++) cells[i].dump(gp);
1259   gzclose(gp);
1260   return 1;
1261 }
1262 
dump(gzFile gp) const1263 void Cell::dump(gzFile gp) const {
1264   int32_t data[8];
1265   for (int i = 0; i < 5; i++) data[i] = saveInt((int32_t)(heights[i] / 0.0025));
1266   gzwrite(gp, data, sizeof(int32_t) * 5);
1267   for (int i = 0; i < 5; i++) {
1268     data[0] = saveInt((int32_t)(colors[i].f0() / 0.01));
1269     data[1] = saveInt((int32_t)(colors[i].f1() / 0.01));
1270     data[2] = saveInt((int32_t)(colors[i].f2() / 0.01));
1271     data[3] = saveInt((int32_t)(colors[i].f3() / 0.01));
1272     gzwrite(gp, data, sizeof(int32_t) * 4);
1273   }
1274   data[0] = saveInt((int32_t)flags);
1275   data[1] = saveInt((int32_t)texture);
1276   gzwrite(gp, data, sizeof(int32_t) * 2);
1277 
1278   for (int i = 0; i < 4; i++) {
1279     data[0] = saveInt((int32_t)(wallColors[i].f0() / 0.01));
1280     data[1] = saveInt((int32_t)(wallColors[i].f1() / 0.01));
1281     data[2] = saveInt((int32_t)(wallColors[i].f2() / 0.01));
1282     data[3] = saveInt((int32_t)(wallColors[i].f3() / 0.01));
1283     gzwrite(gp, data, sizeof(int32_t) * 4);
1284   }
1285 
1286   data[0] = saveInt((int32_t)(velocity[0] / 0.01));
1287   data[1] = saveInt((int32_t)(velocity[1] / 0.01));
1288   gzwrite(gp, data, sizeof(int32_t) * 2);
1289 
1290   for (int i = 0; i < 5; i++) data[i] = saveInt((int32_t)(waterHeights[i] / 0.0025));
1291   gzwrite(gp, data, sizeof(int32_t) * 5);
1292 
1293   for (int i = 0; i < 4; i++) {
1294     /* used to refer to texture coordinates */
1295     data[i * 2] = 0;
1296     data[i * 2 + 1] = 0;
1297   }
1298   gzwrite(gp, data, sizeof(int32_t) * 8);
1299 }
1300 
load(Map * map,gzFile gp,int version)1301 void Cell::load(Map* map, gzFile gp, int version) {
1302   int32_t data[8];
1303 
1304   gzread(gp, data, sizeof(int32_t) * 5);
1305   for (int i = 0; i < 5; i++) heights[i] = 0.0025 * loadInt(data[i]);
1306   for (int i = 0; i < 5; i++) {
1307     if (version < 4) {
1308       // old maps do not have an alpha channel defined
1309       gzread(gp, data, sizeof(int32_t) * 3);
1310       for (int j = 0; j < 3; j++) colors[i].v[j] = 65535 * 0.01 * loadInt(data[j]);
1311       colors[i].v[3] = 65535;
1312     } else {
1313       gzread(gp, data, sizeof(int32_t) * 4);
1314       for (int j = 0; j < 4; j++) colors[i].v[j] = 65535 * 0.01 * loadInt(data[j]);
1315     }
1316   }
1317 
1318   gzread(gp, data, sizeof(int32_t) * 2);
1319   flags = loadInt(data[0]);
1320   if (version <= 1) flags = flags & (CELL_ICE | CELL_ACID);
1321   int k = loadInt(data[1]);
1322   texture = (k >= 0 && k < numTextures ? map->indexTranslation[k] : -1);
1323   // in older maps, this field was not initialized
1324   if (version < 5) texture = -1;
1325   if (version < 3) { /* Old maps do not have wallColors defined */
1326     for (int i = 0; i < 4; i++) { wallColors[i] = Color(0.7, 0.2, 0.2, 1.0); }
1327   } else {
1328     for (int i = 0; i < 4; i++) {
1329       if (version < 4) {
1330         // old maps do not have an alpha channel defined
1331         gzread(gp, data, sizeof(int32_t) * 3);
1332         for (int j = 0; j < 3; j++) wallColors[i].v[j] = 65535 * 0.01 * loadInt(data[j]);
1333         wallColors[i].v[3] = 65535;
1334       } else {
1335         gzread(gp, data, sizeof(int32_t) * 4);
1336         for (int j = 0; j < 4; j++) wallColors[i].v[j] = 65535 * 0.01 * loadInt(data[j]);
1337       }
1338     }
1339   }
1340 
1341   if (version >= 5) {
1342     gzread(gp, data, sizeof(int32_t) * 2);
1343     velocity[0] = 0.01 * loadInt(data[0]);
1344     velocity[1] = 0.01 * loadInt(data[1]);
1345   } else {
1346     velocity[0] = 0.0;
1347     velocity[1] = 0.0;
1348   }
1349 
1350   // currently we just reset the ground water
1351   if (version >= 6) {
1352     gzread(gp, data, sizeof(int32_t) * 5);
1353     for (int i = 0; i < 5; i++) waterHeights[i] = 0.0025 * loadInt(data[i]);
1354   } else {
1355     for (int i = 0; i < 5; i++) waterHeights[i] = heights[i] - 0.5;
1356   }
1357   sunken = 0.0;
1358 
1359   if (version >= 7) {
1360     gzread(gp, data, sizeof(int32_t) * 8);
1361     /* used to be texture coordinates; now is free space */
1362   }
1363 }
1364