Copyright (C) 2010-2011 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 General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+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 General Public License for more details.
+GNU Lesser General Public License for more details.
-You should have received a copy of the GNU General Public License along
+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 "gamedef.h"
#include "mesh.h"
#include "content_mapblock.h"
+#include "noise.h"
+#include "shader.h"
+#include "settings.h"
+#include "util/directiontables.h"
+
+float srgb_linear_multiply(float f, float m, float max)
+{
+ f = f * f; // SRGB -> Linear
+ f *= m;
+ f = sqrt(f); // Linear -> SRGB
+ if(f > max)
+ f = max;
+ return f;
+}
/*
MeshMakeData
// Get map
Map *map = block->getParent();
- for(u16 i=0; i<6; i++)
+ for(u16 i=0; i<26; i++)
{
- const v3s16 &dir = g_6dirs[i];
+ const v3s16 &dir = g_26dirs[i];
v3s16 bp = m_blockpos + dir;
MapBlock *b = map->getBlockNoCreateNoEx(bp);
if(b)
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)
+ //return decode_light(undiminish_light(light_source));
+ if(light_source > light)
+ //return decode_light(light_source);
+ light = light_source;
+
// Make some nice difference to different sides
// This makes light come from a corner
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]);
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 /= 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)
{
- ambient_occlusion -= 4;
- light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+ //ambient_occlusion -= 4;
+ //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+ float light_amount = (8 - ambient_occlusion) / 4.0;
+ float light_f = (float)light / 255.0;
+ light_f = pow(light_f, 2.2f); // gamma -> linear space
+ light_f = light_f * light_amount;
+ light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
+ if(light_f > 1.0)
+ light_f = 1.0;
+ light = 255.0 * light_f + 0.5;
}
return light;
};
static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
- v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
+ v3f p, v3s16 dir, v3f scale, u8 light_source, core::array<FastFace> &dest)
{
FastFace face;
float h = tile.texture.size.Y;
face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
- MapBlock_LightColor(alpha, li0),
+ MapBlock_LightColor(alpha, li0, light_source),
core::vector2d<f32>(x0+w*abs_scale, y0+h));
face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
- MapBlock_LightColor(alpha, li1),
+ MapBlock_LightColor(alpha, li1, light_source),
core::vector2d<f32>(x0, y0+h));
face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
- MapBlock_LightColor(alpha, li2),
+ MapBlock_LightColor(alpha, li2, light_source),
core::vector2d<f32>(x0, y0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
- MapBlock_LightColor(alpha, li3),
+ MapBlock_LightColor(alpha, li3, light_source),
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile;
spec.material_flags |= MATERIAL_FLAG_CRACK;
spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
}
+ // If animated, replace tile texture with one without texture atlas
+ if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
+ {
+ spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
+ }
return spec;
}
0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
};
u8 tileindex = dir_to_tile[facedir*8 + dir_i];
- return getNodeTileN(mn, p, tileindex, data);
+
+ // If not rotated or is side tile, we're done
+ if(facedir == 0 || (tileindex != 0 && tileindex != 1))
+ return getNodeTileN(mn, p, tileindex, data);
+
+ // This is the top or bottom tile, and it shall be rotated; thus rotate it
+ TileSpec spec = getNodeTileN(mn, p, tileindex, data);
+ if(tileindex == 0){
+ if(facedir == 1){ // -90
+ std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+ name += "^[transformR270";
+ spec.texture = data->m_gamedef->tsrc()->getTexture(name);
+ }
+ else if(facedir == 2){ // 180
+ spec.texture.pos += spec.texture.size;
+ spec.texture.size *= -1;
+ }
+ else if(facedir == 3){ // 90
+ std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+ name += "^[transformR90";
+ spec.texture = data->m_gamedef->tsrc()->getTexture(name);
+ }
+ }
+ else if(tileindex == 1){
+ if(facedir == 1){ // -90
+ std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+ name += "^[transformR90";
+ spec.texture = data->m_gamedef->tsrc()->getTexture(name);
+ }
+ else if(facedir == 2){ // 180
+ spec.texture.pos += spec.texture.size;
+ spec.texture.size *= -1;
+ }
+ else if(facedir == 3){ // 90
+ std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+ name += "^[transformR270";
+ spec.texture = data->m_gamedef->tsrc()->getTexture(name);
+ }
+ }
+ return spec;
}
static void getTileInfo(
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
u16 *lights,
- TileSpec &tile
+ TileSpec &tile,
+ u8 &light_source
)
{
VoxelManipulator &vmanip = data->m_vmanip;
tile = tile0;
p_corrected = p;
face_dir_corrected = face_dir;
+ light_source = ndef->get(n0).light_source;
}
else
{
tile = tile1;
p_corrected = p + face_dir;
face_dir_corrected = -face_dir;
+ light_source = ndef->get(n1).light_source;
}
// eg. water and glass
if(equivalent)
tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
-
+
if(data->m_smooth_lighting == false)
{
lights[0] = lights[1] = lights[2] = lights[3] =
v3s16 face_dir_corrected;
u16 lights[4] = {0,0,0,0};
TileSpec tile;
+ u8 light_source = 0;
getTileInfo(data, p, face_dir,
makes_face, p_corrected, face_dir_corrected,
- lights, tile);
+ lights, tile, light_source);
for(u16 j=0; j<MAP_BLOCKSIZE; j++)
{
v3s16 next_face_dir_corrected;
u16 next_lights[4] = {0,0,0,0};
TileSpec next_tile;
+ u8 next_light_source = 0;
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
getTileInfo(data, p_next, face_dir,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
- next_tile);
+ next_tile, next_light_source);
if(next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir
&& next_lights[1] == lights[1]
&& next_lights[2] == lights[2]
&& next_lights[3] == lights[3]
- && next_tile == tile)
+ && next_tile == tile
+ && next_light_source == light_source)
{
next_is_different = false;
}
}
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
- sp, face_dir_corrected, scale,
+ sp, face_dir_corrected, scale, light_source,
dest);
g_profiler->avg("Meshgen: faces drawn by tiling", 0);
lights[2] = next_lights[2];
lights[3] = next_lights[3];
tile = next_tile;
+ light_source = next_light_source;
}
p = p_next;
Convert MeshCollector to SMesh
Also store animation info
*/
+ bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
+ video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
+ getShader("test_shader_1").material;
+ video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
+ getShader("test_shader_2").material;
for(u32 i = 0; i < collector.prebuffers.size(); i++)
{
PreMeshBuffer &p = collector.prebuffers[i];
crack_basename += "^[crack";
m_crack_materials.insert(std::make_pair(i, crack_basename));
}
- // - Lighting
- for(u32 j = 0; j < p.vertices.size(); j++)
+ // - Texture animation
+ if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
{
- video::SColor &vc = p.vertices[j].Color;
- u8 day = vc.getRed();
- u8 night = vc.getGreen();
- finalColorBlend(vc, day, night, 1000);
- if(day != night)
- m_daynight_diffs[i][j] = std::make_pair(day, night);
+ ITextureSource *tsrc = data->m_gamedef->tsrc();
+ // Add to MapBlockMesh in order to animate these tiles
+ m_animation_tiles[i] = p.tile;
+ m_animation_frames[i] = 0;
+ if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
+ // Get starting position from noise
+ m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
+ data->m_blockpos.X, data->m_blockpos.Y,
+ data->m_blockpos.Z, 0));
+ } else {
+ // Play all synchronized
+ m_animation_frame_offsets[i] = 0;
+ }
+ // Replace tile texture with the first animation frame
+ std::ostringstream os(std::ios::binary);
+ os<<tsrc->getTextureName(p.tile.texture.id);
+ os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
+ p.tile.texture = tsrc->getTexture(os.str());
+ }
+ // - Classic lighting (shaders handle this by themselves)
+ if(!enable_shaders)
+ {
+ for(u32 j = 0; j < p.vertices.size(); j++)
+ {
+ video::SColor &vc = p.vertices[j].Color;
+ // Set initial real color and store for later updates
+ u8 day = vc.getRed();
+ u8 night = vc.getGreen();
+ finalColorBlend(vc, day, night, 1000);
+ if(day != night)
+ m_daynight_diffs[i][j] = std::make_pair(day, night);
+ // Brighten topside (no shaders)
+ if(p.vertices[j].Normal.Y > 0.5)
+ {
+ vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
+ vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
+ vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
+ }
+ }
}
-
// Create material
video::SMaterial material;
material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.setTexture(0, p.tile.texture.atlas);
- p.tile.applyMaterialOptions(material);
+ if(enable_shaders)
+ p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2);
+ else
+ p.tile.applyMaterialOptions(material);
// Create meshbuffer
// Check if animation is required for this mesh
m_has_animation =
!m_crack_materials.empty() ||
- !m_daynight_diffs.empty();
+ !m_daynight_diffs.empty() ||
+ !m_animation_tiles.empty();
}
MapBlockMesh::~MapBlockMesh()
m_last_crack = crack;
}
+
+ // Texture animation
+ for(std::map<u32, TileSpec>::iterator
+ i = m_animation_tiles.begin();
+ i != m_animation_tiles.end(); i++)
+ {
+ const TileSpec &tile = i->second;
+ // Figure out current frame
+ int frameoffset = m_animation_frame_offsets[i->first];
+ int frame = (int)(time * 1000 / tile.animation_frame_length_ms
+ + frameoffset) % tile.animation_frame_count;
+ // If frame doesn't change, skip
+ if(frame == m_animation_frames[i->first])
+ continue;
+
+ m_animation_frames[i->first] = frame;
+
+ scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ ITextureSource *tsrc = m_gamedef->getTextureSource();
+
+ // Create new texture name from original
+ std::ostringstream os(std::ios::binary);
+ os<<tsrc->getTextureName(tile.texture.id);
+ os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
+ // Set the texture
+ AtlasPointer ap = tsrc->getTexture(os.str());
+ buf->getMaterial().setTexture(0, ap.atlas);
+ }
// Day-night transition
if(daynight_ratio != m_last_daynight_ratio)
u8 night = j->second.second;
finalColorBlend(vertices[vertexIndex].Color,
day, night, daynight_ratio);
+ // Brighten topside (no shaders)
+ if(vertices[vertexIndex].Normal.Y > 0.5)
+ {
+ video::SColor &vc = vertices[vertexIndex].Color;
+ vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
+ vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
+ vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
+ }
}
}
m_last_daynight_ratio = daynight_ratio;