/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
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
(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
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mapblock_mesh.h"
#include "light.h"
#include "mapblock.h"
#include "map.h"
#include "main.h" // for g_profiler
#include "profiler.h"
#include "nodedef.h"
#include "gamedef.h"
#include "mesh.h"
#include "content_mapblock.h"
#include "noise.h"
#include "shader.h"
#include "settings.h"
#include "util/directiontables.h"
static void applyFacesShading(video::SColor& color, float factor)
{
color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
}
/*
MeshMakeData
*/
MeshMakeData::MeshMakeData(IGameDef *gamedef):
m_vmanip(),
m_blockpos(-1337,-1337,-1337),
m_crack_pos_relative(-1337, -1337, -1337),
m_highlighted_pos_relative(-1337, -1337, -1337),
m_smooth_lighting(false),
m_show_hud(false),
m_highlight_mesh_color(255, 255, 255, 255),
m_gamedef(gamedef)
{}
void MeshMakeData::fill(MapBlock *block)
{
m_blockpos = block->getPos();
v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
/*
Copy data
*/
// Allocate this block + neighbors
m_vmanip.clear();
m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
{
//TimeTaker timer("copy central block data");
// 0ms
// Copy our data
block->copyTo(m_vmanip);
}
{
//TimeTaker timer("copy neighbor block data");
// 0ms
/*
Copy neighbors. This is lightning fast.
Copying only the borders would be *very* slow.
*/
// Get map
Map *map = block->getParent();
for(u16 i=0; i<26; i++)
{
const v3s16 &dir = g_26dirs[i];
v3s16 bp = m_blockpos + dir;
MapBlock *b = map->getBlockNoCreateNoEx(bp);
if(b)
b->copyTo(m_vmanip);
}
}
}
void MeshMakeData::fillSingleNode(MapNode *node)
{
m_blockpos = v3s16(0,0,0);
v3s16 blockpos_nodes = v3s16(0,0,0);
VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
s32 volume = area.getVolume();
s32 our_node_index = area.index(1,1,1);
// Allocate this block + neighbors
m_vmanip.clear();
m_vmanip.addArea(area);
// Fill in data
MapNode *data = new MapNode[volume];
for(s32 i = 0; i < volume; i++)
{
if(i == our_node_index)
{
data[i] = *node;
}
else
{
data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
}
}
m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
delete[] data;
}
void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
{
if(crack_level >= 0)
m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
}
void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
{
m_show_hud = show_hud;
m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
}
void MeshMakeData::setSmoothLighting(bool smooth_lighting)
{
m_smooth_lighting = smooth_lighting;
}
/*
Light and vertex color functions
*/
/*
Calculate non-smooth lighting at interior of node.
Single light bank.
*/
static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
INodeDefManager *ndef)
{
u8 light = n.getLight(bank, ndef);
while(increment > 0)
{
light = undiminish_light(light);
--increment;
}
while(increment < 0)
{
light = diminish_light(light);
++increment;
}
return decode_light(light);
}
/*
Calculate non-smooth lighting at interior of node.
Both light banks.
*/
u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
{
u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
return day | (night << 8);
}
/*
Calculate non-smooth lighting at face of node.
Single light bank.
*/
static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
v3s16 face_dir, INodeDefManager *ndef)
{
u8 light;
u8 l1 = n.getLight(bank, ndef);
u8 l2 = n2.getLight(bank, ndef);
if(l1 > l2)
light = l1;
else
light = l2;
// Boost light level for light sources
u8 light_source = MYMAX(ndef->get(n).light_source,
ndef->get(n2).light_source);
if(light_source > light)
light = light_source;
return decode_light(light);
}
/*
Calculate non-smooth lighting at face of node.
Both light banks.
*/
u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
{
u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
return day | (night << 8);
}
/*
Calculate smooth lighting at the XYZ- corner of p.
Single light bank.
*/
static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
{
static v3s16 dirs8[8] = {
v3s16(0,0,0),
v3s16(0,0,1),
v3s16(0,1,0),
v3s16(0,1,1),
v3s16(1,0,0),
v3s16(1,1,0),
v3s16(1,0,1),
v3s16(1,1,1),
};
INodeDefManager *ndef = data->m_gamedef->ndef();
u16 ambient_occlusion = 0;
u16 light = 0;
u16 light_count = 0;
u8 light_source_max = 0;
for(u32 i=0; i<8; i++)
{
MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
// if it's CONTENT_IGNORE we can't do any light calculations
if (n.getContent() == CONTENT_IGNORE) {
continue;
}
const ContentFeatures &f = ndef->get(n);
if(f.light_source > light_source_max)
light_source_max = f.light_source;
// Check f.solidness because fast-style leaves look
// better this way
if(f.param_type == CPT_LIGHT && f.solidness != 2)
{
light += decode_light(n.getLight(bank, ndef));
light_count++;
}
else {
ambient_occlusion++;
}
}
if(light_count == 0)
return 255;
light /= light_count;
// Boost brightness around light sources
if(decode_light(light_source_max) >= light)
//return decode_light(undiminish_light(light_source_max));
return decode_light(light_source_max);
if(ambient_occlusion > 4)
{
//calculate table index for gamma space multiplier
ambient_occlusion -= 5;
//table of precalculated gamma space multiply factors
|