minetest/src/nodedef.cpp
Matthew I 1d7408a7b8 Fix black display inside opaque water
Opaque water's solidness was being set to 2, like a normal node.
When you swim, it is treated like a solid block, and the display
goes black.  Setting it to 1 like transparent water allows you to
see.

It looks somewhat awkward when you swim, look up, and see an opaque
wall of water (the surface), but there isn't much that can be done
about it.  If you made the water transparent so it looked good,
it would defeat the purpose :) .
2012-08-31 17:50:12 +03:00

762 lines
20 KiB
C++

/*
Minetest-c55
Copyright (C) 2010 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 "nodedef.h"
#include "main.h" // For g_settings
#include "itemdef.h"
#ifndef SERVER
#include "tile.h"
#endif
#include "log.h"
#include "settings.h"
#include "nameidmapping.h"
#include "util/serialize.h"
/*
NodeBox
*/
void NodeBox::reset()
{
type = NODEBOX_REGULAR;
// default is empty
fixed.clear();
// default is sign/ladder-like
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
}
void NodeBox::serialize(std::ostream &os) const
{
writeU8(os, 1); // version
writeU8(os, type);
if(type == NODEBOX_FIXED)
{
writeU16(os, fixed.size());
for(std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); i++)
{
writeV3F1000(os, i->MinEdge);
writeV3F1000(os, i->MaxEdge);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
writeV3F1000(os, wall_top.MinEdge);
writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge);
writeV3F1000(os, wall_bottom.MaxEdge);
writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge);
}
}
void NodeBox::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 1)
throw SerializationError("unsupported NodeBox version");
reset();
type = (enum NodeBoxType)readU8(is);
if(type == NODEBOX_FIXED)
{
u16 fixed_count = readU16(is);
while(fixed_count--)
{
aabb3f box;
box.MinEdge = readV3F1000(is);
box.MaxEdge = readV3F1000(is);
fixed.push_back(box);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
wall_top.MinEdge = readV3F1000(is);
wall_top.MaxEdge = readV3F1000(is);
wall_bottom.MinEdge = readV3F1000(is);
wall_bottom.MaxEdge = readV3F1000(is);
wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is);
}
}
/*
TileDef
*/
void TileDef::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
os<<serializeString(name);
writeU8(os, animation.type);
writeU16(os, animation.aspect_w);
writeU16(os, animation.aspect_h);
writeF1000(os, animation.length);
}
void TileDef::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 0)
throw SerializationError("unsupported TileDef version");
name = deSerializeString(is);
animation.type = (TileAnimationType)readU8(is);
animation.aspect_w = readU16(is);
animation.aspect_h = readU16(is);
animation.length = readF1000(is);
}
/*
SimpleSoundSpec serialization
*/
static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
std::ostream &os)
{
os<<serializeString(ss.name);
writeF1000(os, ss.gain);
}
static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
{
ss.name = deSerializeString(is);
ss.gain = readF1000(is);
}
/*
ContentFeatures
*/
ContentFeatures::ContentFeatures()
{
reset();
}
ContentFeatures::~ContentFeatures()
{
}
void ContentFeatures::reset()
{
/*
Cached stuff
*/
#ifndef SERVER
solidness = 2;
visual_solidness = 0;
backface_culling = true;
#endif
has_on_construct = false;
has_on_destruct = false;
has_after_destruct = false;
/*
Actual data
NOTE: Most of this is always overridden by the default values given
in builtin.lua
*/
name = "";
groups.clear();
// Unknown nodes can be dug
groups["dig_immediate"] = 2;
drawtype = NDT_NORMAL;
visual_scale = 1.0;
for(u32 i=0; i<6; i++)
tiledef[i] = TileDef();
for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
tiledef_special[j] = TileDef();
alpha = 255;
post_effect_color = video::SColor(0, 0, 0, 0);
param_type = CPT_NONE;
param_type_2 = CPT2_NONE;
is_ground_content = false;
light_propagates = false;
sunlight_propagates = false;
walkable = true;
pointable = true;
diggable = true;
climbable = false;
buildable_to = false;
liquid_type = LIQUID_NONE;
liquid_alternative_flowing = "";
liquid_alternative_source = "";
liquid_viscosity = 0;
light_source = 0;
damage_per_second = 0;
node_box = NodeBox();
selection_box = NodeBox();
legacy_facedir_simple = false;
legacy_wallmounted = false;
sound_footstep = SimpleSoundSpec();
sound_dig = SimpleSoundSpec("__group");
sound_dug = SimpleSoundSpec();
}
void ContentFeatures::serialize(std::ostream &os)
{
writeU8(os, 5); // version
os<<serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
i = groups.begin(); i != groups.end(); i++){
os<<serializeString(i->first);
writeS16(os, i->second);
}
writeU8(os, drawtype);
writeF1000(os, visual_scale);
writeU8(os, 6);
for(u32 i=0; i<6; i++)
tiledef[i].serialize(os);
writeU8(os, CF_SPECIAL_COUNT);
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
tiledef_special[i].serialize(os);
}
writeU8(os, alpha);
writeU8(os, post_effect_color.getAlpha());
writeU8(os, post_effect_color.getRed());
writeU8(os, post_effect_color.getGreen());
writeU8(os, post_effect_color.getBlue());
writeU8(os, param_type);
writeU8(os, param_type_2);
writeU8(os, is_ground_content);
writeU8(os, light_propagates);
writeU8(os, sunlight_propagates);
writeU8(os, walkable);
writeU8(os, pointable);
writeU8(os, diggable);
writeU8(os, climbable);
writeU8(os, buildable_to);
os<<serializeString(""); // legacy: used to be metadata_name
writeU8(os, liquid_type);
os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, light_source);
writeU32(os, damage_per_second);
node_box.serialize(os);
selection_box.serialize(os);
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
serializeSimpleSoundSpec(sound_footstep, os);
serializeSimpleSoundSpec(sound_dig, os);
serializeSimpleSoundSpec(sound_dug, os);
}
void ContentFeatures::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 5)
throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is);
groups.clear();
u32 groups_size = readU16(is);
for(u32 i=0; i<groups_size; i++){
std::string name = deSerializeString(is);
int value = readS16(is);
groups[name] = value;
}
drawtype = (enum NodeDrawType)readU8(is);
visual_scale = readF1000(is);
if(readU8(is) != 6)
throw SerializationError("unsupported tile count");
for(u32 i=0; i<6; i++)
tiledef[i].deSerialize(is);
if(readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for(u32 i=0; i<CF_SPECIAL_COUNT; i++)
tiledef_special[i].deSerialize(is);
alpha = readU8(is);
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
post_effect_color.setGreen(readU8(is));
post_effect_color.setBlue(readU8(is));
param_type = (enum ContentParamType)readU8(is);
param_type_2 = (enum ContentParamType2)readU8(is);
is_ground_content = readU8(is);
light_propagates = readU8(is);
sunlight_propagates = readU8(is);
walkable = readU8(is);
pointable = readU8(is);
diggable = readU8(is);
climbable = readU8(is);
buildable_to = readU8(is);
deSerializeString(is); // legacy: used to be metadata_name
liquid_type = (enum LiquidType)readU8(is);
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is);
light_source = readU8(is);
damage_per_second = readU32(is);
node_box.deSerialize(is);
selection_box.deSerialize(is);
legacy_facedir_simple = readU8(is);
legacy_wallmounted = readU8(is);
deSerializeSimpleSoundSpec(sound_footstep, is);
deSerializeSimpleSoundSpec(sound_dig, is);
deSerializeSimpleSoundSpec(sound_dug, is);
// If you add anything here, insert it primarily inside the try-catch
// block to not need to increase the version.
try{
}catch(SerializationError &e) {};
}
/*
CNodeDefManager
*/
class CNodeDefManager: public IWritableNodeDefManager
{
public:
void clear()
{
m_name_id_mapping.clear();
m_name_id_mapping_with_aliases.clear();
for(u16 i=0; i<=MAX_CONTENT; i++)
{
ContentFeatures &f = m_content_features[i];
f.reset(); // Reset to defaults
}
// Set CONTENT_AIR
{
ContentFeatures f;
f.name = "air";
f.drawtype = NDT_AIRLIKE;
f.param_type = CPT_LIGHT;
f.light_propagates = true;
f.sunlight_propagates = true;
f.walkable = false;
f.pointable = false;
f.diggable = false;
f.buildable_to = true;
// Insert directly into containers
content_t c = CONTENT_AIR;
m_content_features[c] = f;
addNameIdMapping(c, f.name);
}
// Set CONTENT_IGNORE
{
ContentFeatures f;
f.name = "ignore";
f.drawtype = NDT_AIRLIKE;
f.param_type = CPT_NONE;
f.light_propagates = false;
f.sunlight_propagates = false;
f.walkable = false;
f.pointable = false;
f.diggable = false;
// A way to remove accidental CONTENT_IGNOREs
f.buildable_to = true;
// Insert directly into containers
content_t c = CONTENT_IGNORE;
m_content_features[c] = f;
addNameIdMapping(c, f.name);
}
}
// CONTENT_IGNORE = not found
content_t getFreeId()
{
for(u32 i=0; i<=0xffff; i++){
const ContentFeatures &f = m_content_features[i];
if(f.name == "")
return i;
}
return CONTENT_IGNORE;
}
CNodeDefManager()
{
clear();
}
virtual ~CNodeDefManager()
{
}
virtual IWritableNodeDefManager* clone()
{
CNodeDefManager *mgr = new CNodeDefManager();
for(u16 i=0; i<=MAX_CONTENT; i++)
{
mgr->set(i, get(i));
}
return mgr;
}
virtual const ContentFeatures& get(content_t c) const
{
assert(c <= MAX_CONTENT);
return m_content_features[c];
}
virtual const ContentFeatures& get(const MapNode &n) const
{
return get(n.getContent());
}
virtual bool getId(const std::string &name, content_t &result) const
{
std::map<std::string, content_t>::const_iterator
i = m_name_id_mapping_with_aliases.find(name);
if(i == m_name_id_mapping_with_aliases.end())
return false;
result = i->second;
return true;
}
virtual content_t getId(const std::string &name) const
{
content_t id = CONTENT_IGNORE;
getId(name, id);
return id;
}
virtual void getIds(const std::string &name, std::set<content_t> &result)
const
{
if(name.substr(0,6) != "group:"){
content_t id = CONTENT_IGNORE;
if(getId(name, id))
result.insert(id);
return;
}
std::string group = name.substr(6);
for(u16 id=0; id<=MAX_CONTENT; id++)
{
const ContentFeatures &f = m_content_features[id];
if(f.name == "") // Quickly discard undefined nodes
continue;
if(itemgroup_get(f.groups, group) != 0)
result.insert(id);
}
}
virtual const ContentFeatures& get(const std::string &name) const
{
content_t id = CONTENT_IGNORE;
getId(name, id);
return get(id);
}
// IWritableNodeDefManager
virtual void set(content_t c, const ContentFeatures &def)
{
verbosestream<<"registerNode: registering content id \""<<c
<<"\": name=\""<<def.name<<"\""<<std::endl;
assert(c <= MAX_CONTENT);
// Don't allow redefining CONTENT_IGNORE (but allow air)
if(def.name == "ignore" || c == CONTENT_IGNORE){
infostream<<"registerNode: WARNING: Ignoring "
<<"CONTENT_IGNORE redefinition"<<std::endl;
return;
}
// Check that the special contents are not redefined as different id
// because it would mess up everything
if((def.name == "ignore" && c != CONTENT_IGNORE) ||
(def.name == "air" && c != CONTENT_AIR)){
errorstream<<"registerNode: IGNORING ERROR: "
<<"trying to register built-in type \""
<<def.name<<"\" as different id"<<std::endl;
return;
}
m_content_features[c] = def;
if(def.name != "")
addNameIdMapping(c, def.name);
}
virtual content_t set(const std::string &name,
const ContentFeatures &def)
{
assert(name == def.name);
u16 id = CONTENT_IGNORE;
bool found = m_name_id_mapping.getId(name, id); // ignore aliases
if(!found){
// Get some id
id = getFreeId();
if(id == CONTENT_IGNORE)
return CONTENT_IGNORE;
if(name != "")
addNameIdMapping(id, name);
}
set(id, def);
return id;
}
virtual content_t allocateDummy(const std::string &name)
{
assert(name != "");
ContentFeatures f;
f.name = name;
return set(name, f);
}
virtual void updateAliases(IItemDefManager *idef)
{
std::set<std::string> all = idef->getAll();
m_name_id_mapping_with_aliases.clear();
for(std::set<std::string>::iterator
i = all.begin(); i != all.end(); i++)
{
std::string name = *i;
std::string convert_to = idef->getAlias(name);
content_t id;
if(m_name_id_mapping.getId(convert_to, id))
{
m_name_id_mapping_with_aliases.insert(
std::make_pair(name, id));
}
}
}
virtual void updateTextures(ITextureSource *tsrc)
{
#ifndef SERVER
infostream<<"CNodeDefManager::updateTextures(): Updating "
<<"textures in node definitions"<<std::endl;
bool new_style_water = g_settings->getBool("new_style_water");
bool new_style_leaves = g_settings->getBool("new_style_leaves");
bool opaque_water = g_settings->getBool("opaque_water");
for(u16 i=0; i<=MAX_CONTENT; i++)
{
ContentFeatures *f = &m_content_features[i];
// Figure out the actual tiles to use
TileDef tiledef[6];
for(u32 j=0; j<6; j++)
{
tiledef[j] = f->tiledef[j];
if(tiledef[j].name == "")
tiledef[j].name = "unknown_block.png";
}
switch(f->drawtype){
default:
case NDT_NORMAL:
f->solidness = 2;
break;
case NDT_AIRLIKE:
f->solidness = 0;
break;
case NDT_LIQUID:
assert(f->liquid_type == LIQUID_SOURCE);
if(opaque_water)
f->alpha = 255;
if(new_style_water){
f->solidness = 0;
} else {
f->solidness = 1;
f->backface_culling = false;
}
break;
case NDT_FLOWINGLIQUID:
assert(f->liquid_type == LIQUID_FLOWING);
f->solidness = 0;
if(opaque_water)
f->alpha = 255;
break;
case NDT_GLASSLIKE:
f->solidness = 0;
f->visual_solidness = 1;
break;
case NDT_ALLFACES:
f->solidness = 0;
f->visual_solidness = 1;
break;
case NDT_ALLFACES_OPTIONAL:
if(new_style_leaves){
f->drawtype = NDT_ALLFACES;
f->solidness = 0;
f->visual_solidness = 1;
} else {
f->drawtype = NDT_NORMAL;
f->solidness = 2;
for(u32 i=0; i<6; i++){
tiledef[i].name += std::string("^[noalpha");
}
}
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
case NDT_PLANTLIKE:
case NDT_FENCELIKE:
case NDT_RAILLIKE:
case NDT_NODEBOX:
f->solidness = 0;
break;
}
// Tiles (fill in f->tiles[])
for(u16 j=0; j<6; j++){
// Texture
f->tiles[j].texture = tsrc->getTexture(tiledef[j].name);
// Alpha
f->tiles[j].alpha = f->alpha;
if(f->alpha == 255)
f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material flags
f->tiles[j].material_flags = 0;
if(f->backface_culling)
f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
if(tiledef[j].animation.type == TAT_VERTICAL_FRAMES)
f->tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
// Animation parameters
if(f->tiles[j].material_flags &
MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
{
// Get raw texture size to determine frame count by
// aspect ratio
video::ITexture *t = tsrc->getTextureRaw(tiledef[j].name);
v2u32 size = t->getOriginalSize();
int frame_height = (float)size.X /
(float)tiledef[j].animation.aspect_w *
(float)tiledef[j].animation.aspect_h;
int frame_count = size.Y / frame_height;
int frame_length_ms = 1000.0 *
tiledef[j].animation.length / frame_count;
f->tiles[j].animation_frame_count = frame_count;
f->tiles[j].animation_frame_length_ms = frame_length_ms;
// If there are no frames for an animation, switch
// animation off (so that having specified an animation
// for something but not using it in the texture pack
// gives no overhead)
if(frame_count == 1){
f->tiles[j].material_flags &=
~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
}
}
}
// Special tiles (fill in f->special_tiles[])
for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
// Texture
f->special_tiles[j].texture =
tsrc->getTexture(f->tiledef_special[j].name);
// Alpha
f->special_tiles[j].alpha = f->alpha;
if(f->alpha == 255)
f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material flags
f->special_tiles[j].material_flags = 0;
if(f->tiledef_special[j].backface_culling)
f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
if(f->tiledef_special[j].animation.type == TAT_VERTICAL_FRAMES)
f->special_tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
// Animation parameters
if(f->special_tiles[j].material_flags &
MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
{
// Get raw texture size to determine frame count by
// aspect ratio
video::ITexture *t = tsrc->getTextureRaw(f->tiledef_special[j].name);
v2u32 size = t->getOriginalSize();
int frame_height = (float)size.X /
(float)f->tiledef_special[j].animation.aspect_w *
(float)f->tiledef_special[j].animation.aspect_h;
int frame_count = size.Y / frame_height;
int frame_length_ms = 1000.0 *
f->tiledef_special[j].animation.length / frame_count;
f->special_tiles[j].animation_frame_count = frame_count;
f->special_tiles[j].animation_frame_length_ms = frame_length_ms;
// If there are no frames for an animation, switch
// animation off (so that having specified an animation
// for something but not using it in the texture pack
// gives no overhead)
if(frame_count == 1){
f->special_tiles[j].material_flags &=
~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
}
}
}
}
#endif
}
void serialize(std::ostream &os)
{
writeU8(os, 1); // version
u16 count = 0;
std::ostringstream os2(std::ios::binary);
for(u16 i=0; i<=MAX_CONTENT; i++)
{
if(i == CONTENT_IGNORE || i == CONTENT_AIR)
continue;
ContentFeatures *f = &m_content_features[i];
if(f->name == "")
continue;
writeU16(os2, i);
// Wrap it in a string to allow different lengths without
// strict version incompatibilities
std::ostringstream wrapper_os(std::ios::binary);
f->serialize(wrapper_os);
os2<<serializeString(wrapper_os.str());
count++;
}
writeU16(os, count);
os<<serializeLongString(os2.str());
}
void deSerialize(std::istream &is)
{
clear();
int version = readU8(is);
if(version != 1)
throw SerializationError("unsupported NodeDefinitionManager version");
u16 count = readU16(is);
std::istringstream is2(deSerializeLongString(is), std::ios::binary);
for(u16 n=0; n<count; n++){
u16 i = readU16(is2);
if(i > MAX_CONTENT){
errorstream<<"ContentFeatures::deSerialize(): "
<<"Too large content id: "<<i<<std::endl;
continue;
}
/*// Do not deserialize special types
if(i == CONTENT_IGNORE || i == CONTENT_AIR)
continue;*/
ContentFeatures *f = &m_content_features[i];
// Read it from the string wrapper
std::string wrapper = deSerializeString(is2);
std::istringstream wrapper_is(wrapper, std::ios::binary);
f->deSerialize(wrapper_is);
verbosestream<<"deserialized "<<f->name<<std::endl;
if(f->name != "")
addNameIdMapping(i, f->name);
}
}
private:
void addNameIdMapping(content_t i, std::string name)
{
m_name_id_mapping.set(i, name);
m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
}
private:
// Features indexed by id
ContentFeatures m_content_features[MAX_CONTENT+1];
// A mapping for fast converting back and forth between names and ids
NameIdMapping m_name_id_mapping;
// Like m_name_id_mapping, but only from names to ids, and includes
// item aliases too. Updated by updateAliases()
// Note: Not serialized.
std::map<std::string, content_t> m_name_id_mapping_with_aliases;
};
IWritableNodeDefManager* createNodeDefManager()
{
return new CNodeDefManager();
}