]> git.lizzy.rs Git - minetest.git/blob - src/voxel.cpp
fe176a27a7d55d0998432dff54af85f3f07bd92d
[minetest.git] / src / voxel.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "voxel.h"
21 #include "map.h"
22
23 VoxelManipulator::VoxelManipulator():
24         m_data(NULL),
25         m_flags(NULL)
26 {
27 }
28
29 VoxelManipulator::~VoxelManipulator()
30 {
31         clear();
32         if(m_data)
33                 delete[] m_data;
34         if(m_flags)
35                 delete[] m_flags;
36 }
37
38 void VoxelManipulator::clear()
39 {
40         // Reset area to volume=0
41         m_area = VoxelArea();
42         if(m_data)
43                 delete[] m_data;
44         m_data = NULL;
45         if(m_flags)
46                 delete[] m_flags;
47         m_flags = NULL;
48 }
49
50 void VoxelManipulator::print(std::ostream &o)
51 {
52         v3s16 em = m_area.getExtent();
53         v3s16 of = m_area.MinEdge;
54         o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
55          <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
56         
57         for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
58         {
59                 if(em.X >= 3 && em.Y >= 3)
60                 {
61                         if     (y==m_area.MinEdge.Y+2) o<<"^     ";
62                         else if(y==m_area.MinEdge.Y+1) o<<"|     ";
63                         else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
64                         else                           o<<"      ";
65                 }
66
67                 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
68                 {
69                         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
70                         {
71                                 u8 f = m_flags[m_area.index(x,y,z)];
72                                 char c;
73                                 if(f & VOXELFLAG_NOT_LOADED)
74                                         c = 'N';
75                                 else if(f & VOXELFLAG_INEXISTENT)
76                                         c = 'I';
77                                 else
78                                 {
79                                         c = 'X';
80                                         u8 m = m_data[m_area.index(x,y,z)].d;
81                                         if(m <= 9)
82                                                 c = m + '0';
83                                 }
84                                 o<<c;
85                         }
86                         o<<' ';
87                 }
88                 o<<std::endl;
89         }
90 }
91
92 void VoxelManipulator::addArea(VoxelArea area)
93 {
94         // Cancel if requested area has zero volume
95         if(area.getExtent() == v3s16(0,0,0))
96                 return;
97         
98         // Cancel if m_area already contains the requested area
99         if(m_area.contains(area))
100                 return;
101         
102         // Calculate new area
103         VoxelArea new_area;
104         // New area is the requested area if m_area has zero volume
105         if(m_area.getExtent() == v3s16(0,0,0))
106         {
107                 new_area = area;
108         }
109         // Else add requested area to m_area
110         else
111         {
112                 new_area = m_area;
113                 new_area.addArea(area);
114         }
115
116         s32 new_size = new_area.getVolume();
117
118         /*dstream<<"adding area ";
119         area.print(dstream);
120         dstream<<", old area ";
121         m_area.print(dstream);
122         dstream<<", new area ";
123         new_area.print(dstream);
124         dstream<<", new_size="<<new_size;
125         dstream<<std::endl;*/
126
127         // Allocate and clear new data
128         MapNode *new_data = new MapNode[new_size];
129         u8 *new_flags = new u8[new_size];
130         for(s32 i=0; i<new_size; i++)
131         {
132                 new_flags[i] = VOXELFLAG_NOT_LOADED;
133         }
134         
135         // Copy old data
136         
137         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
138         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
139         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
140         {
141                 // If loaded, copy data and flags
142                 if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
143                 {
144                         new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
145                         new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
146                 }
147         }
148
149         // Replace area, data and flags
150         
151         m_area = new_area;
152         
153         MapNode *old_data = m_data;
154         u8 *old_flags = m_flags;
155
156         /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
157         <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
158
159         m_data = new_data;
160         m_flags = new_flags;
161         
162         if(old_data)
163                 delete[] old_data;
164         if(old_flags)
165                 delete[] old_flags;
166 }
167
168 void VoxelManipulator::interpolate(VoxelArea area)
169 {
170         VoxelArea emerge_area = area;
171         emerge_area.MinEdge -= v3s16(1,1,1);
172         emerge_area.MaxEdge += v3s16(1,1,1);
173         emerge(emerge_area);
174
175         SharedBuffer<u8> buf(area.getVolume());
176
177         for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++)
178         for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++)
179         for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++)
180         {
181                 v3s16 p(x,y,z);
182
183                 v3s16 dirs[] = {
184                         v3s16(1,1,0),
185                         v3s16(1,0,1),
186                         v3s16(1,-1,0),
187                         v3s16(1,0,-1),
188                         v3s16(-1,1,0),
189                         v3s16(-1,0,1),
190                         v3s16(-1,-1,0),
191                         v3s16(-1,0,-1),
192                 };
193                 //const v3s16 *dirs = g_26dirs;
194                 
195                 s16 total = 0;
196                 s16 airness = 0;
197                 u8 m = MATERIAL_IGNORE;
198
199                 for(s16 i=0; i<8; i++)
200                 //for(s16 i=0; i<26; i++)
201                 {
202                         v3s16 p2 = p + dirs[i];
203
204                         u8 f = m_flags[m_area.index(p2)];
205                         assert(!(f & VOXELFLAG_NOT_LOADED));
206                         if(f & VOXELFLAG_INEXISTENT)
207                                 continue;
208
209                         MapNode &n = m_data[m_area.index(p2)];
210
211                         airness += (n.d == MATERIAL_AIR) ? 1 : -1;
212                         total++;
213
214                         if(m == MATERIAL_IGNORE && n.d != MATERIAL_AIR)
215                                 m = n.d;
216                 }
217
218                 // 1 if air, 0 if not
219                 buf[area.index(p)] = airness > -total/2 ? MATERIAL_AIR : m;
220                 //buf[area.index(p)] = airness > -total ? MATERIAL_AIR : m;
221                 //buf[area.index(p)] = airness >= -7 ? MATERIAL_AIR : m;
222         }
223
224         for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++)
225         for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++)
226         for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++)
227         {
228                 v3s16 p(x,y,z);
229                 m_data[m_area.index(p)].d = buf[area.index(p)];
230         }
231 }
232
233 void VoxelManipulator::flowWater(v3s16 removed_pos)
234 {
235         v3s16 dirs[6] = {
236                 v3s16(0,1,0), // top
237                 v3s16(-1,0,0), // left
238                 v3s16(1,0,0), // right
239                 v3s16(0,0,-1), // front
240                 v3s16(0,0,1), // back
241                 v3s16(0,-1,0), // bottom
242         };
243
244         v3s16 p;
245
246         // Load neighboring nodes
247         // TODO: A bigger area would be better
248         emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
249
250         s32 i;
251         for(i=0; i<6; i++)
252         {
253                 p = removed_pos + dirs[i];
254                 u8 f = m_flags[m_area.index(p)];
255                 // Inexistent or checked nodes can't move
256                 if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
257                         continue;
258                 MapNode &n = m_data[m_area.index(p)];
259                 // Only liquid nodes can move
260                 if(material_liquid(n.d) == false)
261                         continue;
262                 // If block is at top, select it always
263                 if(i == 0)
264                 {
265                         break;
266                 }
267                 // If block is at bottom, select it if it has enough pressure
268                 if(i == 5)
269                 {
270                         if(n.pressure >= 3)
271                                 break;
272                         continue;
273                 }
274                 // Else block is at some side. Select it if it has enough pressure
275                 if(n.pressure >= 2)
276                 {
277                         break;
278                 }
279         }
280
281         // If there is nothing to move, return
282         if(i==6)
283                 return;
284         
285         // Switch nodes at p and removed_pos
286         MapNode n = m_data[m_area.index(p)];
287         u8 f = m_flags[m_area.index(p)];
288         m_data[m_area.index(p)] = m_data[m_area.index(removed_pos)];
289         m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
290         m_data[m_area.index(removed_pos)] = n;
291         m_flags[m_area.index(removed_pos)] = f;
292
293         // Mark p checked
294         m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED;
295         
296         // Update pressure
297         //TODO
298
299         // Flow water to the newly created empty position
300         flowWater(p);
301         
302         // Try flowing water to empty positions around removed_pos.
303         // They are checked in reverse order compared to the previous loop.
304         for(i=5; i>=0; i--)
305         {
306                 p = removed_pos + dirs[i];
307                 u8 f = m_flags[m_area.index(p)];
308                 // Water can't move to inexistent nodes
309                 if(f & VOXELFLAG_INEXISTENT)
310                         continue;
311                 MapNode &n = m_data[m_area.index(p)];
312                 // Water can only move to air
313                 if(n.d != MATERIAL_AIR)
314                         continue;
315                 flowWater(p);
316         }
317 }
318
319 /*
320         MapVoxelManipulator
321 */
322
323 MapVoxelManipulator::MapVoxelManipulator(Map *map)
324 {
325         m_map = map;
326 }
327
328 void MapVoxelManipulator::emerge(VoxelArea a)
329 {
330         v3s16 size = a.getExtent();
331
332         addArea(a);
333
334         for(s16 z=0; z<size.Z; z++)
335         for(s16 y=0; y<size.Y; y++)
336         for(s16 x=0; x<size.X; x++)
337         {
338                 v3s16 p(x,y,z);
339                 s32 i = m_area.index(a.MinEdge + p);
340                 // Don't touch nodes that have already been loaded
341                 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
342                         continue;
343                 try{
344                         MapNode n = m_map->getNode(a.MinEdge + p);
345                         m_data[i] = n;
346                         m_flags[i] = 0;
347                 }
348                 catch(InvalidPositionException &e)
349                 {
350                         m_flags[i] = VOXELFLAG_INEXISTENT;
351                 }
352         }
353 }
354
355 void MapVoxelManipulator::blitBack
356                 (core::map<v3s16, MapBlock*> & modified_blocks)
357 {
358         /*
359                 Initialize block cache
360         */
361         v3s16 blockpos_last;
362         MapBlock *block = NULL;
363         bool block_checked_in_modified = false;
364
365         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
366         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
367         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
368         {
369                 v3s16 p(x,y,z);
370
371                 u8 f = m_flags[m_area.index(p)];
372                 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
373                         continue;
374
375                 MapNode &n = m_data[m_area.index(p)];
376                         
377                 v3s16 blockpos = getNodeBlockPos(p);
378                 
379                 try
380                 {
381                         // Get block
382                         if(block == NULL || blockpos != blockpos_last){
383                                 block = m_map->getBlockNoCreate(blockpos);
384                                 blockpos_last = blockpos;
385                                 block_checked_in_modified = false;
386                         }
387                         
388                         // Calculate relative position in block
389                         v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
390
391                         // Don't continue if nothing has changed here
392                         if(block->getNode(relpos) == n)
393                                 continue;
394
395                         //m_map->setNode(m_area.MinEdge + p, n);
396                         block->setNode(relpos, n);
397                         
398                         /*
399                                 Make sure block is in modified_blocks
400                         */
401                         if(block_checked_in_modified == false)
402                         {
403                                 modified_blocks[blockpos] = block;
404                                 block_checked_in_modified = true;
405                         }
406                 }
407                 catch(InvalidPositionException &e)
408                 {
409                 }
410         }
411 }
412
413 //END