diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 38f19aad..440edd96 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -578,6 +578,22 @@ node definition: ^ The rotation of this node is stored in param2. Plants are rotated this way. Values range 0 - 179. The value stored in param2 is multiplied by two to get the actual rotation of the node. + paramtype2 == "meshoptions" + ^ Only valid for "plantlike". The value of param2 becomes a bitfield which can + be used to change how the client draws plantlike nodes. Bits 0, 1 and 2 form + a mesh selector. Currently the following meshes are choosable: + 0 = a "x" shaped plant (ordinary plant) + 1 = a "+" shaped plant (just rotated 45 degrees) + 2 = a "*" shaped plant with 3 faces instead of 2 + 3 = a "#" shaped plant with 4 faces instead of 2 + 4 = a "#" shaped plant with 4 faces that lean outwards + 5-7 are unused and reserved for future meshes. + Bits 3 through 7 are optional flags that can be combined and give these + effects: + bit 3 (0x08) - Makes the plant slightly vary placement horizontally + bit 4 (0x10) - Makes the plant mesh 1.4x larger + bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) + bits 6-7 are reserved for future use. collision_box = { type = "fixed", fixed = { diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 6a83bd8f..b86b9782 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "gamedef.h" #include "log.h" +#include "noise.h" // Create a cuboid. @@ -1104,6 +1105,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, break;} case NDT_PLANTLIKE: { + PseudoRandom rng(x<<8 | z | y<<16); + TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; @@ -1111,9 +1114,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::SColor c = MapBlock_LightColor(255, l, f.light_source); float s = BS / 2 * f.visual_scale; + // add sqrt(2) visual scale + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0)) + s *= 1.41421; - for (int j = 0; j < 2; j++) - { + float random_offset_X = .0; + float random_offset_Z = .0; + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { + random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + } + + for (int j = 0; j < 4; j++) { video::S3DVertex vertices[4] = { video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1), @@ -1121,28 +1133,146 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0), video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0), }; + float rotate_degree = 0; + u8 p2mesh = 0; if (f.param_type_2 == CPT2_DEGROTATE) rotate_degree = n.param2 * 2; - - if (j == 0) { - for(u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(46 + rotate_degree); - } else if (j == 1) { - for(u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(-44 + rotate_degree); + else if (f.param_type_2 != CPT2_MESHOPTIONS) { + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(46 + rotate_degree); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(-44 + rotate_degree); + } + } else { + p2mesh = n.param2 & 0x7; + switch (p2mesh) { + case 0: + // x + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(46); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(-44); + } + break; + case 1: + // + + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(91); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + } + break; + case 2: + // * + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(121); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(241); + } else { // (j == 2) + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + } + break; + case 3: + // # + switch (j) { + case 0: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(1); + vertices[i].Pos.Z += BS / 4; + } + break; + case 1: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(91); + vertices[i].Pos.X += BS / 4; + } + break; + case 2: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(181); + vertices[i].Pos.Z -= BS / 4; + } + break; + case 3: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(271); + vertices[i].Pos.X -= BS / 4; + } + break; + } + break; + case 4: + // outward leaning #-like + switch (j) { + case 0: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + break; + case 1: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(91); + break; + case 2: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(181); + break; + case 3: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(271); + break; + } + break; + } } - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { vertices[i].Pos *= f.visual_scale; vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); vertices[i].Pos += intToFloat(p, BS); + // move to a random spot to avoid moire + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { + vertices[i].Pos.X += random_offset_X; + vertices[i].Pos.Z += random_offset_Z; + } + // randomly move each face up/down + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) { + PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 ); + vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125); + } } u16 indices[] = {0, 1, 2, 2, 3, 0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); + + // stop adding faces for meshes with less than 4 faces + if (f.param_type_2 == CPT2_MESHOPTIONS) { + if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1)) + break; + else if ((p2mesh == 2) && (j == 2)) + break; + } else if (j == 1) { + break; + } + } break;} case NDT_FIRELIKE: diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 3a9483ef..e3fcae0c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -136,9 +136,11 @@ with this program; if not, write to the Free Software Foundation, Inc., backface_culling: backwards compatibility for playing with newer client on pre-27 servers. Add nodedef v3 - connected nodeboxes + PROTOCOL_VERSION 28: + CPT2_MESHOPTIONS */ -#define LATEST_PROTOCOL_VERSION 27 +#define LATEST_PROTOCOL_VERSION 28 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 764203ef..64637557 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -387,7 +387,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS)) + writeU8(os, CPT2_NONE); + else + writeU8(os, param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); diff --git a/src/nodedef.h b/src/nodedef.h index 1c2f792d..f17c5372 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -65,6 +65,8 @@ enum ContentParamType2 CPT2_LEVELED, // 2D rotation for things like plants CPT2_DEGROTATE, + // Mesh options for plants + CPT2_MESHOPTIONS }; enum LiquidType diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 17f0f0da..379ed773 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -58,6 +58,7 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] = {CPT2_WALLMOUNTED, "wallmounted"}, {CPT2_LEVELED, "leveled"}, {CPT2_DEGROTATE, "degrotate"}, + {CPT2_MESHOPTIONS, "meshoptions"}, {0, NULL}, };