Newer
Older
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Perttu Ahola
committed
This program is free software; you can redistribute it and/or modify
Perttu Ahola
committed
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
Perttu Ahola
committed
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Perttu Ahola
committed
GNU Lesser General Public License for more details.
Perttu Ahola
committed
Perttu Ahola
committed
You should have received a copy of the GNU Lesser General Public License along
Perttu Ahola
committed
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "content_mapblock.h"
Perttu Ahola
committed
#include "main.h" // For g_settings
#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
#include "tile.h"
#include "util/numeric.h"
#include "util/directiontables.h"
Perttu Ahola
committed
// Create a cuboid.
// collector - the MeshCollector for the resulting polygons
// box - the position and size of the box
Kahrl
committed
// tiles - the tiles (materials) to use (for all 6 faces)
// tilecount - number of entries in tiles, 1<=tilecount<=6
Perttu Ahola
committed
// c - vertex colour - used for all
// txc - texture coordinates - this is a list of texture coordinates
// for the opposite corners of each face - therefore, there
// should be (2+2)*6=24 values in the list. Alternatively, pass
// NULL to use the entire texture for each face. The order of
// the faces in the list is up-down-right-left-back-front
// (compatible with ContentFeatures). If you specified 0,0,1,1
// for each face, that would be the same as passing NULL.
void makeCuboid(MeshCollector *collector, const aabb3f &box,
Kahrl
committed
const TileSpec *tiles, int tilecount,
video::SColor &c, const f32* txc)
Perttu Ahola
committed
{
Kahrl
committed
assert(tilecount >= 1 && tilecount <= 6);
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
if(txc == NULL)
Perttu Ahola
committed
{
static const f32 txc_default[24] = {
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1
};
txc = txc_default;
}
Perttu Ahola
committed
video::S3DVertex vertices[24] =
Perttu Ahola
committed
{
// up
video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
// down
video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
// right
video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
// left
video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
// back
video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
// front
video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
};
Perttu Ahola
committed
for(s32 j=0; j<24; j++)
{
Kahrl
committed
int tileindex = MYMIN(j/4, tilecount-1);
vertices[j].TCoords *= tiles[tileindex].texture.size;
vertices[j].TCoords += tiles[tileindex].texture.pos;
}
Perttu Ahola
committed
u16 indices[] = {0,1,2,2,3,0};
Perttu Ahola
committed
// Add to mesh collector
for(s32 j=0; j<24; j+=4)
{
Kahrl
committed
int tileindex = MYMIN(j/4, tilecount-1);
collector->append(tiles[tileindex],
vertices+j, 4, indices, 6);
Perttu Ahola
committed
}
}
void mapblock_mesh_generate_special(MeshMakeData *data,
Kahrl
committed
MeshCollector &collector)
Perttu Ahola
committed
{
Kahrl
committed
INodeDefManager *nodedef = data->m_gamedef->ndef();
Perttu Ahola
committed
// 0ms
//TimeTaker timer("mapblock_mesh_generate_special()");
/*
Some settings
*/
bool new_style_water = g_settings->getBool("new_style_water");
Perttu Ahola
committed
Perttu Ahola
committed
if(new_style_water)
Perttu Ahola
committed
v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
{
v3s16 p(x,y,z);
MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
const ContentFeatures &f = nodedef->get(n);
if(f.solidness != 0)
switch(f.drawtype){
default:
infostream<<"Got "<<f.drawtype<<std::endl;
assert(0);
break;
case NDT_AIRLIKE:
break;
case NDT_LIQUID:
Perttu Ahola
committed
{
/*
Add water sources to mesh if using new style
*/
Kahrl
committed
TileSpec tile_liquid = f.special_tiles[0];
TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
Kahrl
committed
AtlasPointer &pa_liquid = tile_liquid.texture;
bool top_is_same_liquid = false;
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
content_t c_source = nodedef->getId(f.liquid_alternative_source);
if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
top_is_same_liquid = true;
Kahrl
committed
u16 l = getInteriorLight(n, 0, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source));
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/*
Generate sides
*/
v3s16 side_dirs[4] = {
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
};
for(u32 i=0; i<4; i++)
{
v3s16 dir = side_dirs[i];
MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
content_t neighbor_content = neighbor.getContent();
const ContentFeatures &n_feat = nodedef->get(neighbor_content);
MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
content_t n_top_c = n_top.getContent();
if(neighbor_content == CONTENT_IGNORE)
continue;
/*
If our topside is liquid and neighbor's topside
is liquid, don't draw side face
*/
if(top_is_same_liquid && (n_top_c == c_flowing ||
n_top_c == c_source || n_top_c == CONTENT_IGNORE))
continue;
// Don't draw face if neighbor is blocking the view
if(n_feat.solidness == 2)
continue;
bool neighbor_is_same_liquid = (neighbor_content == c_source
|| neighbor_content == c_flowing);
// Don't draw any faces if neighbor same is liquid and top is
// same liquid
if(neighbor_is_same_liquid && !top_is_same_liquid)
continue;
// Use backface culled material if neighbor doesn't have a
// solidness of 0
const TileSpec *current_tile = &tile_liquid;
if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
current_tile = &tile_liquid_bfculled;
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2,0,0,0, c,
pa_liquid.x0(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2,0,0,0, c,
pa_liquid.x1(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
pa_liquid.x1(), pa_liquid.y0()),
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
pa_liquid.x0(), pa_liquid.y0()),
};
/*
If our topside is liquid, set upper border of face
at upper border of node
*/
if(top_is_same_liquid)
{
vertices[2].Pos.Y = 0.5*BS;
vertices[3].Pos.Y = 0.5*BS;
}
/*
Otherwise upper position of face is liquid level
*/
else
{
vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
}
/*
If neighbor is liquid, lower border of face is liquid level
*/
if(neighbor_is_same_liquid)
{
vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
}
/*
If neighbor is not liquid, lower border of face is
lower border of node
*/
else
{
vertices[0].Pos.Y = -0.5*BS;
vertices[1].Pos.Y = -0.5*BS;
}
for(s32 j=0; j<4; j++)
{
if(dir == v3s16(0,0,1))
vertices[j].Pos.rotateXZBy(0);
if(dir == v3s16(0,0,-1))
vertices[j].Pos.rotateXZBy(180);
if(dir == v3s16(-1,0,0))
vertices[j].Pos.rotateXZBy(90);
if(dir == v3s16(1,0,-0))
vertices[j].Pos.rotateXZBy(-90);
// Do this to not cause glitches when two liquids are
// side-by-side
/*if(neighbor_is_same_liquid == false){
vertices[j].Pos.X *= 0.98;
vertices[j].Pos.Z *= 0.98;
}*/
vertices[j].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(*current_tile, vertices, 4, indices, 6);
}
/*
Generate top
*/
if(top_is_same_liquid)
continue;
Perttu Ahola
committed
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y0()),
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y0()),
Perttu Ahola
committed
};
v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
Perttu Ahola
committed
for(s32 i=0; i<4; i++)
{
Kahrl
committed
vertices[i].Pos += offset;
Perttu Ahola
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile_liquid, vertices, 4, indices, 6);
break;}
case NDT_FLOWINGLIQUID:
Perttu Ahola
committed
{
/*
Add flowing liquid to mesh
*/
Kahrl
committed
TileSpec tile_liquid = f.special_tiles[0];
TileSpec tile_liquid_bfculled = f.special_tiles[1];
AtlasPointer &pa_liquid = tile_liquid.texture;
Perttu Ahola
committed
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
Kahrl
committed
content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
content_t c_source = nodedef->getId(f.liquid_alternative_source);
if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
top_is_same_liquid = true;
Perttu Ahola
committed
Kahrl
committed
u16 l = 0;
Perttu Ahola
committed
// If this liquid emits light and doesn't contain light, draw
// it at what it emits, for an increased effect
u8 light_source = nodedef->get(n).light_source;
if(light_source != 0){
//l = decode_light(undiminish_light(light_source));
l = decode_light(light_source);
l = l | (l<<8);
}
Perttu Ahola
committed
// Use the light of the node on top if possible
Perttu Ahola
committed
else if(nodedef->get(ntop).param_type == CPT_LIGHT)
Kahrl
committed
l = getInteriorLight(ntop, 0, data);
Perttu Ahola
committed
else
Kahrl
committed
l = getInteriorLight(n, 0, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source));
Perttu Ahola
committed
Perttu Ahola
committed
// Includes current node
std::map<v3s16, f32> neighbor_levels;
std::map<v3s16, content_t> neighbor_contents;
std::map<v3s16, u8> neighbor_flags;
Perttu Ahola
committed
v3s16 neighbor_dirs[9] = {
v3s16(0,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(1,0,1),
v3s16(-1,0,-1),
v3s16(1,0,-1),
v3s16(-1,0,1),
};
for(u32 i=0; i<9; i++)
{
content_t content = CONTENT_AIR;
Perttu Ahola
committed
float level = -0.5 * BS;
u8 flags = 0;
// Check neighbor
v3s16 p2 = p + neighbor_dirs[i];
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
Perttu Ahola
committed
{
Perttu Ahola
committed
if(n2.getContent() == c_source)
level = (-0.5+node_liquid_level) * BS;
else if(n2.getContent() == c_flowing)
level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK)
+ 0.5) / (float)LIQUID_LEVEL_SOURCE * node_liquid_level) * BS;
Perttu Ahola
committed
// Check node above neighbor.
// NOTE: This doesn't get executed if neighbor
// doesn't exist
p2.Y += 1;
n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
if(n2.getContent() == c_source ||
n2.getContent() == c_flowing)
flags |= neighborflag_top_is_same_liquid;
Perttu Ahola
committed
}
neighbor_levels[neighbor_dirs[i]] = level;
neighbor_contents[neighbor_dirs[i]] = content;
neighbor_flags[neighbor_dirs[i]] = flags;
Perttu Ahola
committed
}
Perttu Ahola
committed
f32 corner_levels[4];
v3s16 halfdirs[4] = {
v3s16(0,0,0),
v3s16(1,0,0),
v3s16(1,0,1),
v3s16(0,0,1),
};
for(u32 i=0; i<4; i++)
{
v3s16 cornerdir = halfdirs[i];
float cornerlevel = 0;
u32 valid_count = 0;
Perttu Ahola
committed
for(u32 j=0; j<4; j++)
{
v3s16 neighbordir = cornerdir - halfdirs[j];
content_t content = neighbor_contents[neighbordir];
// If top is liquid, draw starting from top of node
if(neighbor_flags[neighbordir] &
neighborflag_top_is_same_liquid)
{
cornerlevel = 0.5*BS;
valid_count = 1;
break;
}
// Source is always the same height
else if(content == c_source)
Perttu Ahola
committed
{
Perttu Ahola
committed
valid_count = 1;
break;
}
Perttu Ahola
committed
{
cornerlevel += neighbor_levels[neighbordir];
valid_count++;
}
else if(content == CONTENT_AIR)
Perttu Ahola
committed
}
Perttu Ahola
committed
cornerlevel /= valid_count;
corner_levels[i] = cornerlevel;
}
/*
Generate sides
*/
v3s16 side_dirs[4] = {
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
};
s16 side_corners[4][2] = {
{1, 2},
{3, 0},
{2, 3},
{0, 1},
};
for(u32 i=0; i<4; i++)
{
v3s16 dir = side_dirs[i];
/*
If our topside is liquid and neighbor's topside
is liquid, don't draw side face
Perttu Ahola
committed
*/
if(top_is_same_liquid &&
neighbor_flags[dir] & neighborflag_top_is_same_liquid)
Perttu Ahola
committed
continue;
content_t neighbor_content = neighbor_contents[dir];
const ContentFeatures &n_feat = nodedef->get(neighbor_content);
Perttu Ahola
committed
// Don't draw face if neighbor is blocking the view
if(n_feat.solidness == 2)
Perttu Ahola
committed
continue;
bool neighbor_is_same_liquid = (neighbor_content == c_source
|| neighbor_content == c_flowing);
Perttu Ahola
committed
// Don't draw any faces if neighbor same is liquid and top is
// same liquid
if(neighbor_is_same_liquid == true
&& top_is_same_liquid == false)
Perttu Ahola
committed
continue;
// Use backface culled material if neighbor doesn't have a
// solidness of 0
Kahrl
committed
const TileSpec *current_tile = &tile_liquid;
if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
Kahrl
committed
current_tile = &tile_liquid_bfculled;
Perttu Ahola
committed
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y0()),
Perttu Ahola
committed
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y0()),
Perttu Ahola
committed
};
/*
Perttu Ahola
committed
at upper border of node
*/
Perttu Ahola
committed
{
vertices[2].Pos.Y = 0.5*BS;
vertices[3].Pos.Y = 0.5*BS;
}
/*
Otherwise upper position of face is corner levels
*/
else
{
vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
}
/*
If neighbor is liquid, lower border of face is corner
liquid levels
Perttu Ahola
committed
*/
if(neighbor_is_same_liquid)
Perttu Ahola
committed
{
vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
}
/*
Perttu Ahola
committed
lower border of node
*/
else
{
vertices[0].Pos.Y = -0.5*BS;
vertices[1].Pos.Y = -0.5*BS;
}
for(s32 j=0; j<4; j++)
{
if(dir == v3s16(0,0,1))
vertices[j].Pos.rotateXZBy(0);
if(dir == v3s16(0,0,-1))
vertices[j].Pos.rotateXZBy(180);
if(dir == v3s16(-1,0,0))
vertices[j].Pos.rotateXZBy(90);
if(dir == v3s16(1,0,-0))
vertices[j].Pos.rotateXZBy(-90);
// Do this to not cause glitches when two liquids are
// side-by-side
Perttu Ahola
committed
/*if(neighbor_is_same_liquid == false){
vertices[j].Pos.X *= 0.98;
vertices[j].Pos.Z *= 0.98;
Perttu Ahola
committed
}*/
Perttu Ahola
committed
Kahrl
committed
vertices[j].Pos += intToFloat(p, BS);
Perttu Ahola
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(*current_tile, vertices, 4, indices, 6);
Perttu Ahola
committed
}
/*
Generate top side, if appropriate
*/
Perttu Ahola
committed
{
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x1(), pa_liquid.y0()),
Perttu Ahola
committed
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
Kahrl
committed
pa_liquid.x0(), pa_liquid.y0()),
Perttu Ahola
committed
};
// To get backface culling right, the vertices need to go
// clockwise around the front of the face. And we happened to
// calculate corner levels in exact reverse order.
Perttu Ahola
committed
s32 corner_resolve[4] = {3,2,1,0};
for(s32 i=0; i<4; i++)
{
Perttu Ahola
committed
//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
s32 j = corner_resolve[i];
vertices[i].Pos.Y += corner_levels[j];
Kahrl
committed
vertices[i].Pos += intToFloat(p, BS);
Perttu Ahola
committed
}
// Default downwards-flowing texture animation goes from
// -Z towards +Z, thus the direction is +Z.
// Rotate texture to make animation go in flow direction
// Positive if liquid moves towards +Z
int dz = (corner_levels[side_corners[3][0]] +
corner_levels[side_corners[3][1]]) -
(corner_levels[side_corners[2][0]] +
corner_levels[side_corners[2][1]]);
int dx = (corner_levels[side_corners[1][0]] +
corner_levels[side_corners[1][1]]) -
(corner_levels[side_corners[0][0]] +
corner_levels[side_corners[0][1]]);
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
// -X
if(-dx >= abs(dz))
{
v2f t = vertices[0].TCoords;
vertices[0].TCoords = vertices[1].TCoords;
vertices[1].TCoords = vertices[2].TCoords;
vertices[2].TCoords = vertices[3].TCoords;
vertices[3].TCoords = t;
}
// +X
if(dx >= abs(dz))
{
v2f t = vertices[0].TCoords;
vertices[0].TCoords = vertices[3].TCoords;
vertices[3].TCoords = vertices[2].TCoords;
vertices[2].TCoords = vertices[1].TCoords;
vertices[1].TCoords = t;
}
// -Z
if(-dz >= abs(dx))
{
v2f t = vertices[0].TCoords;
vertices[0].TCoords = vertices[3].TCoords;
vertices[3].TCoords = vertices[2].TCoords;
vertices[2].TCoords = vertices[1].TCoords;
vertices[1].TCoords = t;
t = vertices[0].TCoords;
vertices[0].TCoords = vertices[3].TCoords;
vertices[3].TCoords = vertices[2].TCoords;
vertices[2].TCoords = vertices[1].TCoords;
vertices[1].TCoords = t;
}
Perttu Ahola
committed
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile_liquid, vertices, 4, indices, 6);
Perttu Ahola
committed
}
break;}
case NDT_GLASSLIKE:
Perttu Ahola
committed
{
Kahrl
committed
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 1, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
Perttu Ahola
committed
for(u32 j=0; j<6; j++)
{
// Check this neighbor
v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
// Don't make face if neighbor is of same type
if(n2.getContent() == n.getContent())
continue;
// The face at Z+
Perttu Ahola
committed
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
Kahrl
committed
ap.x0(), ap.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
Kahrl
committed
ap.x1(), ap.y1()),
Perttu Ahola
committed
video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
Kahrl
committed
ap.x1(), ap.y0()),
Perttu Ahola
committed
video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
Kahrl
committed
ap.x0(), ap.y0()),
Perttu Ahola
committed
};
// Rotations in the g_6dirs format
if(j == 0) // Z+
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
vertices[i].Pos.rotateXZBy(0);
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
vertices[i].Pos.rotateXZBy(-90);
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
vertices[i].Pos.rotateYZBy(90);
else if(j == 5) // X-
for(u16 i=0; i<4; i++)
vertices[i].Pos.rotateXZBy(90);
Perttu Ahola
committed
Kahrl
committed
vertices[i].Pos += intToFloat(p, BS);
Perttu Ahola
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile, vertices, 4, indices, 6);
Perttu Ahola
committed
}
break;}
case NDT_ALLFACES:
Perttu Ahola
committed
{
Kahrl
committed
TileSpec tile_leaves = getNodeTile(n, p,
v3s16(0,0,0), data);
AtlasPointer pa_leaves = tile_leaves.texture;
u16 l = getInteriorLight(n, 1, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
Perttu Ahola
committed
Kahrl
committed
v3f pos = intToFloat(p, BS);
aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
box.MinEdge += pos;
box.MaxEdge += pos;
Kahrl
committed
makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
break;}
case NDT_ALLFACES_OPTIONAL:
// This is always pre-converted to something else
assert(0);
break;
case NDT_TORCHLIKE:
Perttu Ahola
committed
{
v3s16 dir = n.getWallMountedDir(nodedef);
Kahrl
committed
u8 tileindex = 0;
if(dir == v3s16(0,-1,0)){
Kahrl
committed
tileindex = 0; // floor
} else if(dir == v3s16(0,1,0)){
Kahrl
committed
tileindex = 1; // ceiling
// For backwards compatibility
} else if(dir == v3s16(0,0,0)){
Kahrl
committed
tileindex = 0; // floor
} else {
Kahrl
committed
tileindex = 2; // side
Perttu Ahola
committed
Kahrl
committed
TileSpec tile = getNodeTileN(n, p, tileindex, data);
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
Perttu Ahola
committed
Perttu Ahola
committed
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
Perttu Ahola
committed
// Wall at X+ of node
video::S3DVertex vertices[4] =
Perttu Ahola
committed
{
video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
ap.x0(), ap.y1()),
video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
ap.x1(), ap.y1()),
video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
ap.x1(), ap.y0()),
video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
ap.x0(), ap.y0()),
};
Perttu Ahola
committed
for(s32 i=0; i<4; i++)
Perttu Ahola
committed
{
if(dir == v3s16(1,0,0))
vertices[i].Pos.rotateXZBy(0);
if(dir == v3s16(-1,0,0))
vertices[i].Pos.rotateXZBy(180);
if(dir == v3s16(0,0,1))
vertices[i].Pos.rotateXZBy(90);
if(dir == v3s16(0,0,-1))
vertices[i].Pos.rotateXZBy(-90);
if(dir == v3s16(0,-1,0))
vertices[i].Pos.rotateXZBy(45);
if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXZBy(-45);
Perttu Ahola
committed
Kahrl
committed
vertices[i].Pos += intToFloat(p, BS);
Perttu Ahola
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile, vertices, 4, indices, 6);
break;}
case NDT_SIGNLIKE:
Perttu Ahola
committed
{
Kahrl
committed
TileSpec tile = getNodeTileN(n, p, 0, data);
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 0, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
float d = (float)BS/16;
// Wall at X+ of node
video::S3DVertex vertices[4] =
{
video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c,
Perttu Ahola
committed
ap.x0(), ap.y0()),
video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c,
ap.x1(), ap.y0()),
video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c,
ap.x1(), ap.y1()),
video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c,
ap.x0(), ap.y1()),
Perttu Ahola
committed
v3s16 dir = n.getWallMountedDir(nodedef);
Sebastian Rühl
committed
for(s32 i=0; i<4; i++)
{
if(dir == v3s16(1,0,0))
vertices[i].Pos.rotateXZBy(0);
if(dir == v3s16(-1,0,0))
vertices[i].Pos.rotateXZBy(180);
if(dir == v3s16(0,0,1))
vertices[i].Pos.rotateXZBy(90);
if(dir == v3s16(0,0,-1))
vertices[i].Pos.rotateXZBy(-90);
if(dir == v3s16(0,-1,0))
vertices[i].Pos.rotateXYBy(-90);
if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXYBy(90);
Sebastian Rühl
committed
Kahrl
committed
vertices[i].Pos += intToFloat(p, BS);
Sebastian Rühl
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile, vertices, 4, indices, 6);
break;}
case NDT_PLANTLIKE:
Sebastian Rühl
committed
{
Kahrl
committed
TileSpec tile = getNodeTileN(n, p, 0, data);
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
Kahrl
committed
u16 l = getInteriorLight(n, 1, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
Sebastian Rühl
committed
Jürgen Doser
committed
for(u32 j=0; j<2; j++)
Sebastian Rühl
committed
{
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
Kahrl
committed
ap.x0(), ap.y1()),
video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
Kahrl
committed
ap.x1(), ap.y1()),
video::S3DVertex( BS/2*f.visual_scale,
-BS/2 + f.visual_scale*BS,0, 0,0,0, c,
Kahrl
committed
ap.x1(), ap.y0()),
video::S3DVertex(-BS/2*f.visual_scale,
-BS/2 + f.visual_scale*BS,0, 0,0,0, c,
Kahrl
committed
ap.x0(), ap.y0()),
Sebastian Rühl
committed
};
if(j == 0)
Perttu Ahola
committed
{
for(u16 i=0; i<4; i++)
Sebastian Rühl
committed
vertices[i].Pos.rotateXZBy(45);
Perttu Ahola
committed
}
Sebastian Rühl
committed
else if(j == 1)
Perttu Ahola
committed
{
for(u16 i=0; i<4; i++)
Sebastian Rühl
committed
vertices[i].Pos.rotateXZBy(-45);
}
Perttu Ahola
committed
for(u16 i=0; i<4; i++)
{
vertices[i].Pos *= f.visual_scale;
Kahrl
committed
vertices[i].Pos += intToFloat(p, BS);
Perttu Ahola
committed
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
Kahrl
committed
collector.append(tile, vertices, 4, indices, 6);
Perttu Ahola
committed
}
break;}
case NDT_FENCELIKE:
Kahrl
committed
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
TileSpec tile_nocrack = tile;
tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
// A hack to put wood the right way around in the posts
ITextureSource *tsrc = data->m_gamedef->tsrc();
TileSpec tile_rot = tile;
tile_rot.texture = tsrc->getTexture(tsrc->getTextureName(
tile.texture.id) + "^[transformR90");
Kahrl
committed
u16 l = getInteriorLight(n, 1, data);
Perttu Ahola
committed
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
const f32 post_rad=(f32)BS/8;
const f32 bar_rad=(f32)BS/16;
const f32 bar_len=(f32)(BS/2)-post_rad;
Kahrl
committed
v3f pos = intToFloat(p, BS);
// The post - always present
aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
post.MinEdge += pos;
post.MaxEdge += pos;
f32 postuv[24]={
6/16.,6/16.,10/16.,10/16.,
6/16.,6/16.,10/16.,10/16.,
0/16.,0,4/16.,1,
4/16.,0,8/16.,1,
8/16.,0,12/16.,1,
12/16.,0,16/16.,1};
makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
// Now a section of fence, +X, if there's a post there
v3s16 p2 = p;
p2.X++;
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
const ContentFeatures *f2 = &nodedef->get(n2);
if(f2->drawtype == NDT_FENCELIKE)
aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
bar_len+BS/2,bar_rad+BS/4,bar_rad);
bar.MinEdge += pos;
bar.MaxEdge += pos;
f32 xrailuv[24]={
0/16.,2/16.,16/16.,4/16.,
0/16.,4/16.,16/16.,6/16.,
6/16.,6/16.,8/16.,8/16.,
10/16.,10/16.,12/16.,12/16.,
0/16.,8/16.,16/16.,10/16.,
0/16.,14/16.,16/16.,16/16.};
Kahrl
committed
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, xrailuv);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
Kahrl
committed
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, xrailuv);
// Now a section of fence, +Z, if there's a post there
p2 = p;
p2.Z++;
n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
f2 = &nodedef->get(n2);
if(f2->drawtype == NDT_FENCELIKE)
{
aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
bar_rad,bar_rad+BS/4,bar_len+BS/2);
bar.MinEdge += pos;
bar.MaxEdge += pos;
f32 zrailuv[24]={
3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
4/16.,1/16.,6/16.,5/16., // for wood texture instead
0/16.,9/16.,16/16.,11/16.,
0/16.,6/16.,16/16.,8/16.,
6/16.,6/16.,8/16.,8/16.,
10/16.,10/16.,12/16.,12/16.};
Kahrl
committed
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, zrailuv);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
Kahrl
committed
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, zrailuv);
break;}
case NDT_RAILLIKE:
Sebastian Rühl
committed
{
bool is_rail_x [] = { false, false }; /* x-1, x+1 */
bool is_rail_z [] = { false, false }; /* z-1, z+1 */
bool is_rail_z_minus_y [] = { false, false }; /* z-1, z+1; y-1 */
bool is_rail_x_minus_y [] = { false, false }; /* x-1, z+1; y-1 */
bool is_rail_z_plus_y [] = { false, false }; /* z-1, z+1; y+1 */
bool is_rail_x_plus_y [] = { false, false }; /* x-1, x+1; y+1 */
Sebastian Rühl
committed
MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));