Adding particle blend, glow and animation (#4705)

This commit is contained in:
Foghrye4 2016-11-14 18:09:59 +04:00 committed by Zeno-
parent 649448a2a9
commit 93e3555eae
15 changed files with 800 additions and 81 deletions

@ -237,6 +237,43 @@ function math.sign(x, tolerance)
return 0 return 0
end end
--------------------------------------------------------------------------------
-- Video enums and pack function
-- E_BLEND_FACTOR
minetest.ebf = {
zero = 0, -- src & dest (0, 0, 0, 0)
one = 1, -- src & dest (1, 1, 1, 1)
dst_color = 2, -- src (destR, destG, destB, destA)
one_minus_dst_color = 3, -- src (1-destR, 1-destG, 1-destB, 1-destA)
src_color = 4, -- dest (srcR, srcG, srcB, srcA)
one_minus_src_color = 5, -- dest (1-srcR, 1-srcG, 1-srcB, 1-srcA)
src_alpha = 6, -- src & dest (srcA, srcA, srcA, srcA)
one_minus_src_alpha = 7, -- src & dest (1-srcA, 1-srcA, 1-srcA, 1-srcA)
dst_alpha = 8, -- src & dest (destA, destA, destA, destA)
one_minus_dst_alpha = 9, -- src & dest (1-destA, 1-destA, 1-destA, 1-destA)
src_alpha_saturate = 10,-- src (min(srcA, 1-destA), idem, ...)
}
-- E_MODULATE_FUNC
minetest.emfn = {
modulate_1x = 1,
modulate_2x = 2,
modulate_4x = 4,
}
-- E_ALPHA_SOURCE
minetest.eas = {
none = 0,
vertex_color = 1,
texture = 2,
}
-- BlendFunc = source * sourceFactor + dest * destFactor
function minetest.pack_texture_blend_func(srcFact, dstFact, modulate, alphaSource)
return alphaSource * 4096 + modulate * 256 + srcFact * 16 + dstFact
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function get_last_folder(text,count) function get_last_folder(text,count)
local parts = text:split(DIR_DELIM) local parts = text:split(DIR_DELIM)

@ -414,6 +414,119 @@ the word "`alpha`", then each texture pixel will contain the RGB of
`<color>` and the alpha of `<color>` multiplied by the alpha of the `<color>` and the alpha of `<color>` multiplied by the alpha of the
texture pixel. texture pixel.
Particle blend
--------------
Blend function is defined by integer number.
There is a huge number of acceptable blend modificators.
Colour of a resulting pixel calculated using formulae:
red = source_red * source_factor + destination_red * destination_factor
and so on for every channel.
Here is a some examples:
Default value:
material_type_param = 0,
Use this value to disable blending. Texture will be applied to existing pixels
using alpha channel of it. Its recomended to use 1-bit alpha
in that case. This value will leave z-buffer writeable.
Additive blend:
material_type_param = 12641,
Source = src_alpha, destination = one, alpha source is a texture and
vertex_color, modulate_1x.
Black color is completely transparent, white color is completely opaque.
Alpha channel still used to calculate result color, but not nessesary.
'destination = one' means that resulting color will be calculated using
overwritten pixels values.
For example with color of source (our texture) RGBA = (0,192,255,63)
"blue-cyan", 1/4 opaque.
and already rendered pixel color (40,192,0) "dark lime green" we will get color:
R = source_red(0) * source_factor(src_alpha=63/255) +
destination_red(40) * destination_factor(one) =
0 * 63/255 + 40 * 1 = 40
G = 192 * 63/255 + 192 * 1 = 239
B = 255 * 63/255 + 0 * 1 = 63
Result: (40,239,63), "green" (a kind of).
Note, if you made a texture with some kind of shape with colour 662211h
it will appear dark red with a single particle, then yellow with a
several of them and white if player looking thru a lot of them. With
this you could made a nice-looking fire.
Substractive blend:
material_type_param = 12548,
Source = zero, destination = src_color, alpha source is a texture and
vertex_color, modulate_1x.
Texture darkness act like an alpha channel.
Black color is completely opaque, white color is completely transparent.
'destination = src_color' means that destination in multiplied by
a source values. 'source = zero' means that source values ignored
(multiplied by 0).
Invert blend:
material_type_param = 12597,
Source = one_minus_dst_color, destination = one_minus_src_alpha, alpha source
is a texture and vertex_color, modulate_1x.
Pixels invert color if source color value is big enough. If not, they just
black.
'destination = one_minus_src_alpha' means, that effect is masked by a
source alpha channel.
You can design and use your own blend using those enum values and function
'minetest.pack_texture_blend_func'. Returned value of a function is
your 'material_type_param'.
A values in a brackets is a multiplicators of a red, green, blue
and alpha channels respectively.
* 'minetest.ebf': global table, containing blend factor enum values. Such as:
* zero = 0 -- src & dest (0, 0, 0, 0)
* one = 1 -- src & dest (1, 1, 1, 1)
* dst_color = 2 -- src (destR, destG, destB, destA)
* one_minus_dst_color = 3 -- src (1-destR, 1-destG, 1-destB, 1-destA)
* src_color = 4 -- dest (srcR, srcG, srcB, srcA)
* one_minus_src_color = 5 -- dest (1-srcR, 1-srcG, 1-srcB, 1-srcA)
* src_alpha = 6 -- src & dest (srcA, srcA, srcA, srcA)
* one_minus_src_alpha = 7 -- src & dest (1-srcA, 1-srcA, 1-srcA, 1-srcA)
* dst_alpha = 8 -- src & dest (destA, destA, destA, destA)
* one_minus_dst_alpha = 9 -- src & dest (1-destA, 1-destA, 1-destA, 1-destA)
* src_alpha_saturate = 10 -- src (min(srcA, 1-destA), idem, ...)
* 'minetest.emfn': global table, containing modulate enum values.
* Multiply the components of the arguments, and shift the products to the
* left by x bits for brightening. Contain:
* modulate_1x = 1 -- no bit shift
* modulate_2x = 2 -- 1 bits shift
* modulate_4x = 4 -- 2 bits shift
'modulate_4x' is quite useful when you want to make additive blend stronger
with a lower amount of particles.
* 'minetest.eas': global table, containing alpha source enum values. Such as:
* none = 0 -- do not use alpha.
* vertex_color = 1 -- use vertex color alpha.
* texture = 2 -- use texture alpha.
You can use both 'vertex_color' and 'texture' source by using value 3.
* 'minetest.pack_texture_blend_func(srcFact, dstFact, modulate, alphaSource)': return integer
* Pack texture blend funcion variable. Depending from that variable blend
* function will be applied in time of a render poligons with selected material.
* Therefore resulting pixel will be 'source * srcFact + destination * dstFact'
* Use result of this function as 'material_type_param'.
Sounds Sounds
------ ------
Only Ogg Vorbis files are supported. Only Ogg Vorbis files are supported.
@ -3650,7 +3763,7 @@ Definition tables
### Tile definition ### Tile definition
* `"image.png"` * `"image.png"`
* `{name="image.png", animation={Tile Animation definition}}` * `{name="image.png", animation={Animation definition}}`
* `{name="image.png", backface_culling=bool, tileable_vertical=bool, * `{name="image.png", backface_culling=bool, tileable_vertical=bool,
tileable_horizontal=bool}` tileable_horizontal=bool}`
* backface culling enabled by default for most nodes * backface culling enabled by default for most nodes
@ -3661,8 +3774,50 @@ Definition tables
* deprecated, yet still supported field names: * deprecated, yet still supported field names:
* `image` (name) * `image` (name)
### Tile animation definition ### Animation definition
* `{type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}`
#### Node animation, particle and particle spawners
* `{ type="vertical_frames",
aspect_w=16,
-- ^ specify width of a picture in pixels.
aspect_h=16,
-- ^ specify height of a frame in pixels.
length=3.0
-- ^ specify full loop length.
first_frame = 0, -- <- only for particles, use
min_first_frame = 0, -- <- for particle spawners
max_first_frame = 0,
loop_animation = true, -- <- only for particles and particle spawners
-- specify if animation should start from beginning after last frame.
}`
#### Particle and particle spawners only
* `{
type="2d_animation_sheet", -- <- only for particles and particle spawners
vertical_frame_num = 1,
horizontal_frame_num = 1,
-- ^ specify amount of frames in texture.
-- Can be used both for animation or for texture transform
-- together with first_frame variable.
-- A animation texture separated on equal parts of frames,
-- by horizontal and vertical numbers. For example with
-- vertical_frame_num = 4 and horizontal_frame_num = 3 we got
-- 4*3 = 12 frames in total. Animation sequence start from
-- left top frame and go on to the right until reach end of
-- row. Next row also start from left frame.
first_frame = 0, -- <- only for particles, use
min_first_frame = 0, -- <- for particle spawners
max_first_frame = 0,
-- ^ specify first frame to start animation.
frame_length = -1,
-- ^ specify length of a frame in seconds. Negative and zero values
-- disable animation. A sequence with vertical_frame_num = 4 and
-- horizontal_frame_num = 3, first_frame = 4 and frame_length = 0.1
-- will end in (4*3-4)*0.1 = 0.8 seconds.
loop_animation = true,
-- specify if animation should start from beginning after last frame.
}`
* All settings are optional. Default values is located in this example.
### Node definition (`register_node`) ### Node definition (`register_node`)
@ -4117,6 +4272,20 @@ The Biome API is still in an experimental phase and subject to change.
-- ^ Uses texture (string) -- ^ Uses texture (string)
playername = "singleplayer" playername = "singleplayer"
-- ^ optional, if specified spawns particle only on the player's client -- ^ optional, if specified spawns particle only on the player's client
material_type_param = 12641,
-- ^ optional, if specified spawns particle with
-- specified material type param and disable z-buffer.
-- Some examples:
-- Default value: 0,
-- Additive blend: 12641,
-- Substractive blend: 12548,
-- Invert blend: 12597,
-- See also "Particle blend".
animation = {Animation definition},
-- ^ see above. Note, that particle and particle spawners have differences.
glow = 15,
-- ^ optional, specify particle self-luminescence in darkness.
values may vary from 0 (no glow) to 15 (bright glow).
} }
### `ParticleSpawner` definition (`add_particlespawner`) ### `ParticleSpawner` definition (`add_particlespawner`)
@ -4151,6 +4320,20 @@ The Biome API is still in an experimental phase and subject to change.
-- ^ Uses texture (string) -- ^ Uses texture (string)
playername = "singleplayer" playername = "singleplayer"
-- ^ Playername is optional, if specified spawns particle only on the player's client -- ^ Playername is optional, if specified spawns particle only on the player's client
material_type_param = 12641,
-- ^ optional, if specified spawns particle with specified material type
-- param and disable z-buffer.
-- Some examples:
-- Default value: 0,
-- Additive blend: 12641,
-- Substractive blend: 12548,
-- Invert blend: 12597,
-- See also "Particle blend".
animation = {Animation definition},
-- ^ see above. Note, that particle and particle spawners have differences.
glow = 15,
-- ^ optional, specify particle self-luminescence in darkness.
values may vary from 0 (no glow) to 15 (bright glow).
} }
### `HTTPRequest` definition (`HTTPApiTable.fetch_async`, `HTTPApiTable.fetch_async`) ### `HTTPRequest` definition (`HTTPApiTable.fetch_async`, `HTTPApiTable.fetch_async`)

@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h" #include "hud.h"
#include "particles.h" #include "particles.h"
#include "network/networkpacket.h" #include "network/networkpacket.h"
#include "nodedef.h" // AnimationType
struct MeshMakeData; struct MeshMakeData;
class MapBlockMesh; class MapBlockMesh;
@ -185,6 +186,14 @@ struct ClientEvent
bool collision_removal; bool collision_removal;
bool vertical; bool vertical;
std::string *texture; std::string *texture;
u32 material_type_param;
AnimationType animation_type;
u16 vertical_frame_num;
u16 horizontal_frame_num;
u16 first_frame;
float frame_length;
bool loop_animation;
u8 glow;
} spawn_particle; } spawn_particle;
struct{ struct{
u16 amount; u16 amount;
@ -205,6 +214,15 @@ struct ClientEvent
bool vertical; bool vertical;
std::string *texture; std::string *texture;
u32 id; u32 id;
u32 material_type_param;
AnimationType animation_type;
u16 vertical_frame_num;
u16 horizontal_frame_num;
u16 min_first_frame;
u16 max_first_frame;
float frame_length;
bool loop_animation;
u8 glow;
} add_particlespawner; } add_particlespawner;
struct{ struct{
u32 id; u32 id;

@ -896,9 +896,25 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
std::string texture = deSerializeLongString(is); std::string texture = deSerializeLongString(is);
bool vertical = false; bool vertical = false;
bool collision_removal = false; bool collision_removal = false;
u32 material_type_param = 0;
AnimationType animation_type = AT_NONE;
u16 vertical_frame_num = 1;
u16 horizontal_frame_num = 1;
u16 first_frame = 0;
float frame_length = -1;
bool loop_animation = true;
u8 glow = 0;
try { try {
vertical = readU8(is); vertical = readU8(is);
collision_removal = readU8(is); collision_removal = readU8(is);
material_type_param = readU32(is);
animation_type = (AnimationType)readU8(is);
vertical_frame_num = readU16(is);
horizontal_frame_num = readU16(is);
first_frame = readU16(is);
frame_length = readF1000(is);
loop_animation = readU8(is);
glow = readU8(is);
} catch (...) {} } catch (...) {}
ClientEvent event; ClientEvent event;
@ -912,7 +928,14 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
event.spawn_particle.collision_removal = collision_removal; event.spawn_particle.collision_removal = collision_removal;
event.spawn_particle.vertical = vertical; event.spawn_particle.vertical = vertical;
event.spawn_particle.texture = new std::string(texture); event.spawn_particle.texture = new std::string(texture);
event.spawn_particle.material_type_param = material_type_param;
event.spawn_particle.animation_type = animation_type;
event.spawn_particle.vertical_frame_num = vertical_frame_num;
event.spawn_particle.horizontal_frame_num = horizontal_frame_num;
event.spawn_particle.first_frame = first_frame;
event.spawn_particle.frame_length = frame_length;
event.spawn_particle.loop_animation = loop_animation;
event.spawn_particle.glow = glow;
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }
@ -932,6 +955,15 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
float maxsize; float maxsize;
bool collisiondetection; bool collisiondetection;
u32 id; u32 id;
u32 material_type_param = 0;
u8 animation_type = (u8)AT_NONE;
u16 vertical_frame_num = 1;
u16 horizontal_frame_num = 1;
u16 min_first_frame = 0;
u16 max_first_frame = 0;
float frame_length = -1;
bool loop_animation = true;
u8 glow = 0;
*pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
>> minacc >> maxacc >> minexptime >> maxexptime >> minsize >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
@ -948,7 +980,15 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
*pkt >> vertical; *pkt >> vertical;
*pkt >> collision_removal; *pkt >> collision_removal;
*pkt >> attached_id; *pkt >> attached_id;
*pkt >> material_type_param;
*pkt >> animation_type;
*pkt >> vertical_frame_num;
*pkt >> horizontal_frame_num;
*pkt >> min_first_frame;
*pkt >> max_first_frame;
*pkt >> frame_length;
*pkt >> loop_animation;
*pkt >> glow;
} catch (...) {} } catch (...) {}
ClientEvent event; ClientEvent event;
@ -971,6 +1011,15 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
event.add_particlespawner.vertical = vertical; event.add_particlespawner.vertical = vertical;
event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.texture = new std::string(texture);
event.add_particlespawner.id = id; event.add_particlespawner.id = id;
event.add_particlespawner.material_type_param = material_type_param;
event.add_particlespawner.animation_type = (AnimationType)animation_type;
event.add_particlespawner.vertical_frame_num = vertical_frame_num;
event.add_particlespawner.horizontal_frame_num = horizontal_frame_num;
event.add_particlespawner.min_first_frame = min_first_frame;
event.add_particlespawner.max_first_frame = max_first_frame;
event.add_particlespawner.frame_length = frame_length;
event.add_particlespawner.loop_animation = loop_animation;
event.add_particlespawner.glow = glow;
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }

@ -211,7 +211,7 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con
{ {
int version = readU8(is); int version = readU8(is);
name = deSerializeString(is); name = deSerializeString(is);
animation.type = (TileAnimationType)readU8(is); animation.type = (AnimationType)readU8(is);
animation.aspect_w = readU16(is); animation.aspect_w = readU16(is);
animation.aspect_h = readU16(is); animation.aspect_h = readU16(is);
animation.length = readF1000(is); animation.length = readF1000(is);
@ -531,7 +531,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
tile->material_flags = 0; tile->material_flags = 0;
if (backface_culling) if (backface_culling)
tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
if (tiledef->animation.type == TAT_VERTICAL_FRAMES) if (tiledef->animation.type == AT_VERTICAL_FRAMES)
tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
if (tiledef->tileable_horizontal) if (tiledef->tileable_horizontal)
tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;

@ -161,9 +161,10 @@ enum NodeDrawType
/* /*
Stand-alone definition of a TileSpec (basically a server-side TileSpec) Stand-alone definition of a TileSpec (basically a server-side TileSpec)
*/ */
enum TileAnimationType{ enum AnimationType{
TAT_NONE=0, AT_NONE = 0,
TAT_VERTICAL_FRAMES=1, AT_VERTICAL_FRAMES = 1,
AT_2D_ANIMATION_SHEET = 2,
}; };
struct TileDef struct TileDef
{ {
@ -172,7 +173,7 @@ struct TileDef
bool tileable_horizontal; bool tileable_horizontal;
bool tileable_vertical; bool tileable_vertical;
struct{ struct{
enum TileAnimationType type; enum AnimationType type;
int aspect_w; // width for aspect ratio int aspect_w; // width for aspect ratio
int aspect_h; // height for aspect ratio int aspect_h; // height for aspect ratio
float length; // seconds float length; // seconds
@ -184,7 +185,7 @@ struct TileDef
backface_culling = true; backface_culling = true;
tileable_horizontal = true; tileable_horizontal = true;
tileable_vertical = true; tileable_vertical = true;
animation.type = TAT_NONE; animation.type = AT_NONE;
animation.aspect_w = 1; animation.aspect_w = 1;
animation.aspect_h = 1; animation.aspect_h = 1;
animation.length = 1.0; animation.length = 1.0;

@ -43,6 +43,22 @@ v3f random_v3f(v3f min, v3f max)
rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z); rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
} }
u32 check_material_type_param(u32 material_type_param)
{
u32 alphaSource = (material_type_param & 0x0000F000) >> 12;
u32 modulo = (material_type_param & 0x00000F00) >> 8;
u32 srcFact = (material_type_param & 0x000000F0) >> 4;
u32 dstFact = material_type_param & 0x0000000F;
if (alphaSource <= 3 && modulo <= 4 && srcFact <= 10 && dstFact <= 10) {
return material_type_param;
} else {
errorstream << "Server send incorrect ";
errorstream << "material_type_param value for particle.";
errorstream << std::endl;
return 0;
}
}
Particle::Particle( Particle::Particle(
IGameDef *gamedef, IGameDef *gamedef,
scene::ISceneManager* smgr, scene::ISceneManager* smgr,
@ -58,7 +74,14 @@ Particle::Particle(
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
v2f texpos, v2f texpos,
v2f texsize v2f texsize,
u32 material_type_param,
u16 vertical_frame_num,
u16 horizontal_frame_num,
u16 first_frame,
float frame_length,
bool loop_animation,
u8 glow
): ):
scene::ISceneNode(smgr->getRootSceneNode(), smgr) scene::ISceneNode(smgr->getRootSceneNode(), smgr)
{ {
@ -71,11 +94,26 @@ Particle::Particle(
m_material.setFlag(video::EMF_BACK_FACE_CULLING, false); m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
m_material.setFlag(video::EMF_BILINEAR_FILTER, false); m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
m_material.setFlag(video::EMF_FOG_ENABLE, true); m_material.setFlag(video::EMF_FOG_ENABLE, true);
if (material_type_param != 0) {
m_material.MaterialType = video::EMT_ONETEXTURE_BLEND;
m_material.MaterialTypeParam = irr::core::FR(material_type_param);
// We must disable z-buffer if we want to avoid transparent pixels
// to overlap pixels with lower z-value.
m_material.setFlag(video::EMF_ZWRITE_ENABLE, false);
} else {
m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
m_material.setTexture(0, texture); m_material.setTexture(0, texture);
m_texpos = texpos; m_texpos = texpos;
m_texsize = texsize; m_texsize = texsize;
m_vertical_frame_num = vertical_frame_num;
m_horizontal_frame_num = horizontal_frame_num;
m_first_frame = first_frame;
m_frame_length = frame_length;
m_loop_animation = loop_animation;
m_texsize.Y /= m_vertical_frame_num;
m_texsize.X /= m_horizontal_frame_num;
// Particle related // Particle related
m_pos = pos; m_pos = pos;
@ -88,6 +126,7 @@ Particle::Particle(
m_collisiondetection = collisiondetection; m_collisiondetection = collisiondetection;
m_collision_removal = collision_removal; m_collision_removal = collision_removal;
m_vertical = vertical; m_vertical = vertical;
m_glow = glow;
// Irrlicht stuff // Irrlicht stuff
m_collisionbox = aabb3f m_collisionbox = aabb3f
@ -170,16 +209,29 @@ void Particle::updateLight()
else else
light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
m_light = decode_light(light); m_light = decode_light(light + m_glow);
} }
void Particle::updateVertices() void Particle::updateVertices()
{ {
video::SColor c(255, m_light, m_light, m_light); video::SColor c(255, m_light, m_light, m_light);
f32 tx0 = m_texpos.X; u16 frame = m_first_frame;
f32 tx1 = m_texpos.X + m_texsize.X; if (m_frame_length > 0) {
f32 ty0 = m_texpos.Y; if (m_loop_animation)
f32 ty1 = m_texpos.Y + m_texsize.Y; frame = m_first_frame + (u32)(m_time / m_frame_length)
% (m_vertical_frame_num *
m_horizontal_frame_num - m_first_frame);
else if (m_time >=
(m_vertical_frame_num * m_horizontal_frame_num
- m_first_frame) * m_frame_length)
frame = m_vertical_frame_num * m_horizontal_frame_num - 1;
else
frame = m_first_frame + (u16)(m_time / m_frame_length);
}
f32 tx0 = m_texpos.X + m_texsize.X * (frame % m_horizontal_frame_num);
f32 tx1 = m_texpos.X + m_texsize.X * (frame % m_horizontal_frame_num + 1);
f32 ty0 = m_texpos.Y + m_texsize.Y * (frame / m_horizontal_frame_num);
f32 ty1 = m_texpos.Y + m_texsize.Y * (frame / m_horizontal_frame_num + 1);
m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
c, tx0, ty1); c, tx0, ty1);
@ -214,7 +266,16 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
float minexptime, float maxexptime, float minsize, float maxsize, float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical, bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
video::ITexture *texture, u32 id, ParticleManager *p_manager) : video::ITexture *texture, u32 id,
u32 material_type_param,
u16 vertical_frame_num,
u16 horizontal_frame_num,
u16 min_first_frame,
u16 max_first_frame,
float frame_length,
bool loop_animation,
u8 glow,
ParticleManager *p_manager) :
m_particlemanager(p_manager) m_particlemanager(p_manager)
{ {
m_gamedef = gamedef; m_gamedef = gamedef;
@ -238,6 +299,14 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
m_vertical = vertical; m_vertical = vertical;
m_texture = texture; m_texture = texture;
m_time = 0; m_time = 0;
m_vertical_frame_num = vertical_frame_num;
m_horizontal_frame_num = horizontal_frame_num;
m_min_first_frame = min_first_frame;
m_max_first_frame = max_first_frame;
m_frame_length = frame_length;
m_loop_animation = loop_animation;
m_material_type_param = material_type_param;
m_glow = glow;
for (u16 i = 0; i<=m_amount; i++) for (u16 i = 0; i<=m_amount; i++)
{ {
@ -251,7 +320,6 @@ ParticleSpawner::~ParticleSpawner() {}
void ParticleSpawner::step(float dtime, ClientEnvironment* env) void ParticleSpawner::step(float dtime, ClientEnvironment* env)
{ {
m_time += dtime; m_time += dtime;
bool unloaded = false; bool unloaded = false;
v3f attached_offset = v3f(0,0,0); v3f attached_offset = v3f(0,0,0);
if (m_attached_id != 0) { if (m_attached_id != 0) {
@ -285,7 +353,10 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
float size = rand()/(float)RAND_MAX float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize) *(m_maxsize-m_minsize)
+m_minsize; +m_minsize;
u16 first_frame = m_min_first_frame +
rand() %
(m_max_first_frame -
m_min_first_frame + 1);
Particle* toadd = new Particle( Particle* toadd = new Particle(
m_gamedef, m_gamedef,
m_smgr, m_smgr,
@ -301,7 +372,14 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
m_vertical, m_vertical,
m_texture, m_texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
m_material_type_param,
m_vertical_frame_num,
m_horizontal_frame_num,
first_frame,
m_frame_length,
m_loop_animation,
m_glow);
m_particlemanager->addParticle(toadd); m_particlemanager->addParticle(toadd);
} }
i = m_spawntimes.erase(i); i = m_spawntimes.erase(i);
@ -331,7 +409,10 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
float size = rand()/(float)RAND_MAX float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize) *(m_maxsize-m_minsize)
+m_minsize; +m_minsize;
u16 first_frame = m_min_first_frame +
rand() %
(m_max_first_frame -
m_min_first_frame + 1);
Particle* toadd = new Particle( Particle* toadd = new Particle(
m_gamedef, m_gamedef,
m_smgr, m_smgr,
@ -347,7 +428,14 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
m_vertical, m_vertical,
m_texture, m_texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
m_material_type_param,
m_vertical_frame_num,
m_horizontal_frame_num,
first_frame,
m_frame_length,
m_loop_animation,
m_glow);
m_particlemanager->addParticle(toadd); m_particlemanager->addParticle(toadd);
} }
} }
@ -459,6 +547,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
video::ITexture *texture = video::ITexture *texture =
gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
float frame_length = -1;
u16 vertical_frame_num = 1;
u16 horizontal_frame_num = 1;
u32 material_type_param =
check_material_type_param(event->add_particlespawner.material_type_param);
switch (event->add_particlespawner.animation_type) {
case AT_NONE:
break;
case AT_VERTICAL_FRAMES: {
v2u32 size = texture->getOriginalSize();
int frame_height = (float)size.X /
(float)event->add_particlespawner.vertical_frame_num *
(float)event->add_particlespawner.horizontal_frame_num;
vertical_frame_num = size.Y / frame_height;
frame_length =
event->add_particlespawner.frame_length /
vertical_frame_num;
break;
}
case AT_2D_ANIMATION_SHEET: {
vertical_frame_num =
event->add_particlespawner.vertical_frame_num;
horizontal_frame_num =
event->add_particlespawner.horizontal_frame_num;
frame_length =
event->add_particlespawner.frame_length;
break;
}
default:
break;
}
ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player, ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
event->add_particlespawner.amount, event->add_particlespawner.amount,
event->add_particlespawner.spawntime, event->add_particlespawner.spawntime,
@ -478,6 +599,14 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
event->add_particlespawner.vertical, event->add_particlespawner.vertical,
texture, texture,
event->add_particlespawner.id, event->add_particlespawner.id,
material_type_param,
vertical_frame_num,
horizontal_frame_num,
event->add_particlespawner.min_first_frame,
event->add_particlespawner.max_first_frame,
frame_length,
event->add_particlespawner.loop_animation,
event->add_particlespawner.glow,
this); this);
/* delete allocated content of event */ /* delete allocated content of event */
@ -502,6 +631,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
video::ITexture *texture = video::ITexture *texture =
gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
float frame_length = -1;
u16 vertical_frame_num = 1;
u16 horizontal_frame_num = 1;
u32 material_type_param =
check_material_type_param(event->spawn_particle.material_type_param);
switch (event->spawn_particle.animation_type) {
case AT_NONE:
break;
case AT_VERTICAL_FRAMES: {
v2u32 size = texture->getOriginalSize();
int frame_height = (float)size.X /
(float)event->spawn_particle.vertical_frame_num *
(float)event->spawn_particle.horizontal_frame_num;
vertical_frame_num = size.Y / frame_height;
frame_length =
event->spawn_particle.frame_length /
vertical_frame_num;
break;
}
case AT_2D_ANIMATION_SHEET: {
vertical_frame_num =
event->spawn_particle.vertical_frame_num;
horizontal_frame_num =
event->spawn_particle.horizontal_frame_num;
frame_length =
event->spawn_particle.frame_length;
break;
}
default:
break;
}
Particle* toadd = new Particle(gamedef, smgr, player, m_env, Particle* toadd = new Particle(gamedef, smgr, player, m_env,
*event->spawn_particle.pos, *event->spawn_particle.pos,
*event->spawn_particle.vel, *event->spawn_particle.vel,
@ -513,13 +675,21 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
event->spawn_particle.vertical, event->spawn_particle.vertical,
texture, texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
material_type_param,
vertical_frame_num,
horizontal_frame_num,
event->spawn_particle.first_frame,
frame_length,
event->spawn_particle.loop_animation,
event->spawn_particle.glow);
addParticle(toadd); addParticle(toadd);
delete event->spawn_particle.pos; delete event->spawn_particle.pos;
delete event->spawn_particle.vel; delete event->spawn_particle.vel;
delete event->spawn_particle.acc; delete event->spawn_particle.acc;
delete event->spawn_particle.texture;
break; break;
} }
@ -588,7 +758,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
false, false,
texture, texture,
texpos, texpos,
texsize); texsize,
0, 1, 1, 0, -1, true, 0);
addParticle(toadd); addParticle(toadd);
} }

@ -50,7 +50,14 @@ class Particle : public scene::ISceneNode
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
v2f texpos, v2f texpos,
v2f texsize v2f texsize,
u32 material_type_param,
u16 vertical_frame_num,
u16 horizontal_frame_num,
u16 first_frame,
float frame_length,
bool loop_animation,
u8 glow
); );
~Particle(); ~Particle();
@ -102,6 +109,12 @@ private:
bool m_collision_removal; bool m_collision_removal;
bool m_vertical; bool m_vertical;
v3s16 m_camera_offset; v3s16 m_camera_offset;
u16 m_vertical_frame_num;
u16 m_horizontal_frame_num;
u16 m_first_frame;
float m_frame_length;
bool m_loop_animation;
u8 m_glow;
}; };
class ParticleSpawner class ParticleSpawner
@ -123,8 +136,15 @@ class ParticleSpawner
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
u32 id, u32 id,
u32 material_type_param,
u16 vertical_frame_num,
u16 horizontal_frame_num,
u16 min_first_frame,
u16 max_first_frame,
float frame_length,
bool loop_animation,
u8 glow,
ParticleManager* p_manager); ParticleManager* p_manager);
~ParticleSpawner(); ~ParticleSpawner();
void step(float dtime, ClientEnvironment *env); void step(float dtime, ClientEnvironment *env);
@ -156,6 +176,14 @@ class ParticleSpawner
bool m_collision_removal; bool m_collision_removal;
bool m_vertical; bool m_vertical;
u16 m_attached_id; u16 m_attached_id;
u32 m_material_type_param;
u16 m_vertical_frame_num;
u16 m_horizontal_frame_num;
u16 m_min_first_frame;
u16 m_max_first_frame;
float m_frame_length;
bool m_loop_animation;
u8 m_glow;
}; };
/** /**

@ -35,10 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "noise.h" #include "noise.h"
#include <json/json.h> #include <json/json.h>
struct EnumString es_TileAnimationType[] = struct EnumString es_AnimationType[] =
{ {
{TAT_NONE, "none"}, {AT_NONE, "none"},
{TAT_VERTICAL_FRAMES, "vertical_frames"}, {AT_VERTICAL_FRAMES, "vertical_frames"},
{AT_2D_ANIMATION_SHEET, "2d_animation_sheet"},
{0, NULL}, {0, NULL},
}; };
@ -335,9 +336,9 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
lua_getfield(L, index, "animation"); lua_getfield(L, index, "animation");
if(lua_istable(L, -1)){ if(lua_istable(L, -1)){
// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
tiledef.animation.type = (TileAnimationType) tiledef.animation.type = (AnimationType)
getenumfield(L, -1, "type", es_TileAnimationType, getenumfield(L, -1, "type", es_AnimationType,
TAT_NONE); AT_NONE);
tiledef.animation.aspect_w = tiledef.animation.aspect_w =
getintfield_default(L, -1, "aspect_w", 16); getintfield_default(L, -1, "aspect_w", 16);
tiledef.animation.aspect_h = tiledef.animation.aspect_h =

@ -159,6 +159,6 @@ bool push_json_value (lua_State *L,
void read_json_value (lua_State *L, Json::Value &root, void read_json_value (lua_State *L, Json::Value &root,
int index, u8 recursion = 0); int index, u8 recursion = 0);
extern struct EnumString es_TileAnimationType[]; extern struct EnumString es_AnimationType[];
#endif /* C_CONTENT_H_ */ #endif /* C_CONTENT_H_ */

@ -513,6 +513,28 @@ int getintfield_default(lua_State *L, int table,
return result; return result;
} }
int check_material_type_param(lua_State *L, int table,
const char *fieldname, int default_)
{
int material_type_param =
getintfield_default(L, table, fieldname, default_);
u32 alphaSource = (material_type_param & 0x0000F000) >> 12;
u32 modulo = (material_type_param & 0x00000F00) >> 8;
u32 srcFact = (material_type_param & 0x000000F0) >> 4;
u32 dstFact = material_type_param & 0x0000000F;
if (alphaSource <= 3 && modulo <= 4 && srcFact <= 10 && dstFact <= 10) {
return material_type_param;
} else {
std::ostringstream error_text;
error_text << "Incorrect material_type_param value ";
error_text << "for particle or particle spawner.";
error_text << std::endl;
throw LuaError(error_text.str());
return 0;
}
}
float getfloatfield_default(lua_State *L, int table, float getfloatfield_default(lua_State *L, int table,
const char *fieldname, float default_) const char *fieldname, float default_)
{ {

@ -45,6 +45,8 @@ float getfloatfield_default(lua_State *L, int table,
const char *fieldname, float default_); const char *fieldname, float default_);
int getintfield_default (lua_State *L, int table, int getintfield_default (lua_State *L, int table,
const char *fieldname, int default_); const char *fieldname, int default_);
int check_material_type_param(lua_State *L, int table,
const char *fieldname, int default_);
bool getstringfield(lua_State *L, int table, bool getstringfield(lua_State *L, int table,
const char *fieldname, std::string &result); const char *fieldname, std::string &result);

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_object.h" #include "lua_api/l_object.h"
#include "lua_api/l_internal.h" #include "lua_api/l_internal.h"
#include "common/c_converter.h" #include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h" #include "server.h"
#include "particles.h" #include "particles.h"
@ -34,6 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// collision_removal = bool // collision_removal = bool
// vertical = bool // vertical = bool
// texture = e.g."default_wood.png" // texture = e.g."default_wood.png"
// material_type_param = num
// animation = animation definition
// glow = indexed color or color string
int ModApiParticles::l_add_particle(lua_State *L) int ModApiParticles::l_add_particle(lua_State *L)
{ {
MAP_LOCK_REQUIRED; MAP_LOCK_REQUIRED;
@ -44,13 +48,24 @@ int ModApiParticles::l_add_particle(lua_State *L)
float expirationtime, size; float expirationtime, size;
expirationtime = size = 1; expirationtime = size = 1;
float frame_or_loop_length = -1;
AnimationType animation_type = AT_NONE;
u16 vertical_frame_num_or_aspect = 1;
u16 horizontal_frame_num_or_aspect = 1;
u16 first_frame = 0;
bool collisiondetection, vertical, collision_removal; bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false; collisiondetection = vertical = collision_removal = false;
bool loop_animation = true;
std::string texture = ""; std::string texture = "";
std::string playername = ""; std::string playername = "";
u32 material_type_param = 0;
u8 glow = 0;
if (lua_gettop(L) > 1) // deprecated if (lua_gettop(L) > 1) // deprecated
{ {
log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition");
@ -96,6 +111,59 @@ int ModApiParticles::l_add_particle(lua_State *L)
expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); expirationtime = getfloatfield_default(L, 1, "expirationtime", 1);
size = getfloatfield_default(L, 1, "size", 1); size = getfloatfield_default(L, 1, "size", 1);
lua_getfield(L, 1, "animation");
if (lua_istable(L, -1)) {
animation_type = (AnimationType)
getenumfield(L, -1, "type", es_AnimationType,
AT_NONE);
}
switch (animation_type) {
case AT_NONE:
break;
case AT_2D_ANIMATION_SHEET:
frame_or_loop_length =
getfloatfield_default(L, -1, "frame_length", -1);
vertical_frame_num_or_aspect =
getintfield_default(L, -1, "vertical_frame_num", 1);
horizontal_frame_num_or_aspect =
getintfield_default(L, -1, "horizontal_frame_num", 1);
first_frame =
getintfield_default(L, -1, "first_frame", 0);
loop_animation =
getboolfield_default(L, -1, "loop_animation", true);
break;
case AT_VERTICAL_FRAMES:
frame_or_loop_length =
getfloatfield_default(L, -1, "length", -1);
vertical_frame_num_or_aspect =
getintfield_default(L, -1, "aspect_w", 1);
horizontal_frame_num_or_aspect =
getintfield_default(L, -1, "aspect_h", 1);
first_frame =
getintfield_default(L, -1, "first_frame", 0);
loop_animation =
getboolfield_default(L, -1, "loop_animation", true);
break;
default:
break;
}
lua_pop(L, 1);
if (animation_type == AT_2D_ANIMATION_SHEET &&
first_frame >= vertical_frame_num_or_aspect *
horizontal_frame_num_or_aspect) {
std::ostringstream error_text;
error_text << "first_frame should be lower, than "
<< "vertical_frame_num * horizontal_frame_num. "
<< "Got first_frame=" << first_frame
<< ", vertical_frame_num="
<< vertical_frame_num_or_aspect
<< " and horizontal_frame_num="
<< horizontal_frame_num_or_aspect << std::endl;
throw LuaError(error_text.str());
}
collisiondetection = getboolfield_default(L, 1, collisiondetection = getboolfield_default(L, 1,
"collisiondetection", collisiondetection); "collisiondetection", collisiondetection);
collision_removal = getboolfield_default(L, 1, collision_removal = getboolfield_default(L, 1,
@ -103,9 +171,16 @@ int ModApiParticles::l_add_particle(lua_State *L)
vertical = getboolfield_default(L, 1, "vertical", vertical); vertical = getboolfield_default(L, 1, "vertical", vertical);
texture = getstringfield_default(L, 1, "texture", ""); texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", ""); playername = getstringfield_default(L, 1, "playername", "");
material_type_param = check_material_type_param(L, 1, "material_type_param", 0);
glow = getintfield_default (L, 1, "glow", 0);
} }
getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime,
collisiondetection, collision_removal, vertical, texture); size, collisiondetection, collision_removal, vertical,
texture, material_type_param,
animation_type,
vertical_frame_num_or_aspect,
horizontal_frame_num_or_aspect,
first_frame, frame_or_loop_length, loop_animation, glow);
return 1; return 1;
} }
@ -127,21 +202,33 @@ int ModApiParticles::l_add_particle(lua_State *L)
// collision_removal = bool // collision_removal = bool
// vertical = bool // vertical = bool
// texture = e.g."default_wood.png" // texture = e.g."default_wood.png"
// material_type_param = num
// animation = animation definition
// glow = indexed color or color string
int ModApiParticles::l_add_particlespawner(lua_State *L) int ModApiParticles::l_add_particlespawner(lua_State *L)
{ {
MAP_LOCK_REQUIRED; MAP_LOCK_REQUIRED;
// Get parameters // Get parameters
u16 amount = 1; u16 amount = 1;
u16 vertical_frame_num_or_aspect = 1;
u16 horizontal_frame_num_or_aspect = 1;
u16 min_first_frame = 0;
u16 max_first_frame = 0;
v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0); minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0);
float time, minexptime, maxexptime, minsize, maxsize; float time, minexptime, maxexptime, minsize, maxsize;
time= minexptime= maxexptime= minsize= maxsize= 1; time= minexptime= maxexptime= minsize= maxsize= 1;
AnimationType animation_type = AT_NONE;
float frame_or_loop_length = -1;
bool collisiondetection, vertical, collision_removal; bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false; collisiondetection = vertical = collision_removal = false;
bool loop_animation = true;
ServerActiveObject *attached = NULL; ServerActiveObject *attached = NULL;
std::string texture = ""; std::string texture = "";
std::string playername = ""; std::string playername = "";
u32 material_type_param = 0;
u8 glow = 0;
if (lua_gettop(L) > 1) //deprecated if (lua_gettop(L) > 1) //deprecated
{ {
@ -196,6 +283,65 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime); maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime);
minsize = getfloatfield_default(L, 1, "minsize", minsize); minsize = getfloatfield_default(L, 1, "minsize", minsize);
maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); maxsize = getfloatfield_default(L, 1, "maxsize", maxsize);
lua_getfield(L, 1, "animation");
if (lua_istable(L, -1)) {
animation_type = (AnimationType)
getenumfield(L, -1, "type", es_AnimationType,
AT_NONE);
}
switch (animation_type) {
case AT_NONE:
break;
case AT_2D_ANIMATION_SHEET:
frame_or_loop_length =
getfloatfield_default(L, -1, "frame_length", -1);
vertical_frame_num_or_aspect =
getintfield_default(L, -1, "vertical_frame_num", 1);
horizontal_frame_num_or_aspect =
getintfield_default(L, -1, "horizontal_frame_num", 1);
min_first_frame =
getintfield_default(L, -1, "min_first_frame", 0);
max_first_frame =
getintfield_default(L, -1, "max_first_frame", 0);
loop_animation =
getboolfield_default(L, -1, "loop_animation", true);
break;
case AT_VERTICAL_FRAMES:
frame_or_loop_length =
getfloatfield_default(L, -1, "length", -1);
vertical_frame_num_or_aspect =
getintfield_default(L, -1, "aspect_w", 1);
horizontal_frame_num_or_aspect =
getintfield_default(L, -1, "aspect_h", 1);
min_first_frame =
getintfield_default(L, -1, "min_first_frame", 0);
max_first_frame =
getintfield_default(L, -1, "max_first_frame", 0);
loop_animation =
getboolfield_default(L, -1, "loop_animation", true);
break;
default:
break;
}
lua_pop(L, 1);
if (animation_type == AT_2D_ANIMATION_SHEET &&
max_first_frame >= vertical_frame_num_or_aspect *
horizontal_frame_num_or_aspect) {
std::ostringstream error_text;
error_text << "max_first_frame should be lower, than "
<< "vertical_frame_num * horizontal_frame_num. "
<< "Got max_first_frame="
<< max_first_frame
<< ", vertical_frame_num="
<< vertical_frame_num_or_aspect
<< " and horizontal_frame_num="
<< horizontal_frame_num_or_aspect << std::endl;
throw LuaError(error_text.str());
}
collisiondetection = getboolfield_default(L, 1, collisiondetection = getboolfield_default(L, 1,
"collisiondetection", collisiondetection); "collisiondetection", collisiondetection);
collision_removal = getboolfield_default(L, 1, collision_removal = getboolfield_default(L, 1,
@ -211,6 +357,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
vertical = getboolfield_default(L, 1, "vertical", vertical); vertical = getboolfield_default(L, 1, "vertical", vertical);
texture = getstringfield_default(L, 1, "texture", ""); texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", ""); playername = getstringfield_default(L, 1, "playername", "");
material_type_param = check_material_type_param(L, 1, "material_type_param", 0);
glow = getintfield_default(L, 1, "glow", 0);
} }
u32 id = getServer(L)->addParticleSpawner(amount, time, u32 id = getServer(L)->addParticleSpawner(amount, time,
@ -223,9 +371,17 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
collision_removal, collision_removal,
attached, attached,
vertical, vertical,
texture, playername); texture,
playername,
material_type_param,
animation_type,
vertical_frame_num_or_aspect,
horizontal_frame_num_or_aspect,
min_first_frame, max_first_frame,
frame_or_loop_length,
loop_animation,
glow);
lua_pushnumber(L, id); lua_pushnumber(L, id);
return 1; return 1;
} }

@ -1658,7 +1658,11 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, bool collisiondetection, float expirationtime, float size, bool collisiondetection,
bool collision_removal, bool collision_removal,
bool vertical, const std::string &texture) bool vertical, const std::string &texture,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
float frame_length, bool loop_animation,
u8 glow)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
@ -1670,6 +1674,12 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
pkt << vertical; pkt << vertical;
pkt << collision_removal; pkt << collision_removal;
pkt << material_type_param
<< (u8)animation_type
<< vertical_frame_num
<< horizontal_frame_num << first_frame
<< frame_length << loop_animation << glow;
if (peer_id != PEER_ID_INEXISTENT) { if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt); Send(&pkt);
} }
@ -1682,7 +1692,10 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, bool collision_removal, float minsize, float maxsize, bool collisiondetection, bool collision_removal,
u16 attached_id, bool vertical, const std::string &texture, u32 id) u16 attached_id, bool vertical, const std::string &texture, u32 id,
u32 material_type_param, AnimationType animation_type, u16 vertical_frame_num, u16 horizontal_frame_num,
u16 min_first_frame, u16 max_first_frame, float frame_length,
bool loop_animation, u8 glow)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
@ -1698,6 +1711,12 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
pkt << collision_removal; pkt << collision_removal;
pkt << attached_id; pkt << attached_id;
pkt << material_type_param
<< (u8)animation_type
<< vertical_frame_num << horizontal_frame_num
<< min_first_frame << max_first_frame
<< frame_length << loop_animation << glow;
if (peer_id != PEER_ID_INEXISTENT) { if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt); Send(&pkt);
} }
@ -3147,7 +3166,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos,
v3f velocity, v3f acceleration, v3f velocity, v3f acceleration,
float expirationtime, float size, bool float expirationtime, float size, bool
collisiondetection, bool collision_removal, collisiondetection, bool collision_removal,
bool vertical, const std::string &texture) bool vertical, const std::string &texture,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
float frame_length, bool loop_animation,
u8 glow)
{ {
// m_env will be NULL if the server is initializing // m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
@ -3163,7 +3186,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos,
SendSpawnParticle(peer_id, pos, velocity, acceleration, SendSpawnParticle(peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, expirationtime, size, collisiondetection,
collision_removal, vertical, texture); collision_removal, vertical, texture,
material_type_param, animation_type,
vertical_frame_num, horizontal_frame_num,
first_frame, frame_length, loop_animation,
glow);
} }
u32 Server::addParticleSpawner(u16 amount, float spawntime, u32 Server::addParticleSpawner(u16 amount, float spawntime,
@ -3171,7 +3198,9 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
float minexptime, float maxexptime, float minsize, float maxsize, float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
ServerActiveObject *attached, bool vertical, const std::string &texture, ServerActiveObject *attached, bool vertical, const std::string &texture,
const std::string &playername) const std::string &playername, u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num, u16 min_first_frame, u16 max_first_frame,
float frame_length, bool loop_animation, u8 glow)
{ {
// m_env will be NULL if the server is initializing // m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
@ -3197,7 +3226,10 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc, minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize, minexptime, maxexptime, minsize, maxsize,
collisiondetection, collision_removal, attached_id, vertical, collisiondetection, collision_removal, attached_id, vertical,
texture, id); texture, id, material_type_param, animation_type,
vertical_frame_num, horizontal_frame_num,
min_first_frame, max_first_frame, frame_length, loop_animation,
glow);
return id; return id;
} }

@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h" #include "hud.h"
#include "gamedef.h" #include "gamedef.h"
#include "serialization.h" // For SER_FMT_VER_INVALID #include "serialization.h" // For SER_FMT_VER_INVALID
#include "nodedef.h" // AnimationType
#include "mods.h" #include "mods.h"
#include "inventorymanager.h" #include "inventorymanager.h"
#include "subgame.h" #include "subgame.h"
@ -254,7 +255,11 @@ public:
v3f pos, v3f velocity, v3f acceleration, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, float expirationtime, float size,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
bool vertical, const std::string &texture); bool vertical, const std::string &texture,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
float frame_length, bool loop_animation,
u8 glow);
u32 addParticleSpawner(u16 amount, float spawntime, u32 addParticleSpawner(u16 amount, float spawntime,
v3f minpos, v3f maxpos, v3f minpos, v3f maxpos,
@ -265,7 +270,12 @@ public:
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
ServerActiveObject *attached, ServerActiveObject *attached,
bool vertical, const std::string &texture, bool vertical, const std::string &texture,
const std::string &playername); const std::string &playername,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num,
u16 min_first_frame, u16 max_first_frame,
float frame_length, bool loop_animation,
u8 glow);
void deleteParticleSpawner(const std::string &playername, u32 id); void deleteParticleSpawner(const std::string &playername, u32 id);
@ -441,7 +451,12 @@ private:
float minsize, float maxsize, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
u16 attached_id, u16 attached_id,
bool vertical, const std::string &texture, u32 id); bool vertical, const std::string &texture, u32 id,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num,
u16 min_first_frame, u16 max_first_frame,
float frame_length, bool loop_animation,
u8 glow);
void SendDeleteParticleSpawner(u16 peer_id, u32 id); void SendDeleteParticleSpawner(u16 peer_id, u32 id);
@ -450,7 +465,11 @@ private:
v3f pos, v3f velocity, v3f acceleration, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, float expirationtime, float size,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
bool vertical, const std::string &texture); bool vertical, const std::string &texture,
u32 material_type_param, AnimationType animation_type,
u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
float frame_length, bool loop_animation,
u8 glow);
u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas);
void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true);