Merge pull request #9 from minetest/master

Pull particles and other stuff
master
rarkenin 2013-03-23 16:12:55 -07:00
commit 1a2a9cb08f
23 changed files with 1421 additions and 158 deletions

View File

@ -310,6 +310,10 @@ param2 is reserved for the engine when any of these are used:
paramtype2 == "facedir"
^ The rotation of the node is stored in param2. Furnaces and chests are
rotated this way. Can be made by using minetest.dir_to_facedir().
Values range 0 - 23
facedir modulo 4 = axisdir
0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
facedir's two less significant bits are rotation around the axis
Nodes can also contain extra data. See "Node Metadata".
@ -1024,6 +1028,37 @@ minetest.get_ban_description(ip_or_name) -> ban description (string)
minetest.ban_player(name) -> ban a player
minetest.unban_player_or_ip(name) -> unban player or IP address
Particles:
minetest.add_particle(pos, velocity, acceleration, expirationtime,
size, collisiondetection, texture, playername)
^ Spawn particle at pos with velocity and acceleration
^ Disappears after expirationtime seconds
^ collisiondetection: if true collides with physical objects
^ Uses texture (string)
^ Playername is optional, if specified spawns particle only on the player's client
minetest.add_particlespawner(amount, time,
minpos, maxpos,
minvel, maxvel,
minacc, maxacc,
minexptime, maxexptime,
minsize, maxsize,
collisiondetection, texture, playername)
^ Add a particlespawner, an object that spawns an amount of particles over time seconds
^ The particle's properties are random values in between the boundings:
^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
^ minsize/maxsize, minexptime/maxexptime (expirationtime)
^ collisiondetection: if true uses collisiondetection
^ Uses texture (string)
^ Playername is optional, if specified spawns particle only on the player's client
^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
^ Returns and id
minetest.delete_particlespawner(id, player)
^ Delete ParticleSpawner with id (return value from add_particlespawner)
^ If playername is specified, only deletes on the player's client,
^ otherwise on all clients
Random:
minetest.get_connected_players() -> list of ObjectRefs
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer

View File

@ -243,6 +243,7 @@ set(common_SRCS
scriptapi_object.cpp
scriptapi_nodemeta.cpp
scriptapi_inventory.cpp
scriptapi_particles.cpp
scriptapi.cpp
script.cpp
log.cpp

View File

@ -1937,6 +1937,89 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
event.show_formspec.formname = new std::string(formname);
m_client_event_queue.push_back(event);
}
else if(command == TOCLIENT_SPAWN_PARTICLE)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
v3f pos = readV3F1000(is);
v3f vel = readV3F1000(is);
v3f acc = readV3F1000(is);
float expirationtime = readF1000(is);
float size = readF1000(is);
bool collisiondetection = readU8(is);
std::string texture = deSerializeLongString(is);
ClientEvent event;
event.type = CE_SPAWN_PARTICLE;
event.spawn_particle.pos = new v3f (pos);
event.spawn_particle.vel = new v3f (vel);
event.spawn_particle.acc = new v3f (acc);
event.spawn_particle.expirationtime = expirationtime;
event.spawn_particle.size = size;
event.add_particlespawner.collisiondetection =
collisiondetection;
event.spawn_particle.texture = new std::string(texture);
m_client_event_queue.push_back(event);
}
else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u16 amount = readU16(is);
float spawntime = readF1000(is);
v3f minpos = readV3F1000(is);
v3f maxpos = readV3F1000(is);
v3f minvel = readV3F1000(is);
v3f maxvel = readV3F1000(is);
v3f minacc = readV3F1000(is);
v3f maxacc = readV3F1000(is);
float minexptime = readF1000(is);
float maxexptime = readF1000(is);
float minsize = readF1000(is);
float maxsize = readF1000(is);
bool collisiondetection = readU8(is);
std::string texture = deSerializeLongString(is);
u32 id = readU32(is);
ClientEvent event;
event.type = CE_ADD_PARTICLESPAWNER;
event.add_particlespawner.amount = amount;
event.add_particlespawner.spawntime = spawntime;
event.add_particlespawner.minpos = new v3f (minpos);
event.add_particlespawner.maxpos = new v3f (maxpos);
event.add_particlespawner.minvel = new v3f (minvel);
event.add_particlespawner.maxvel = new v3f (maxvel);
event.add_particlespawner.minacc = new v3f (minacc);
event.add_particlespawner.maxacc = new v3f (maxacc);
event.add_particlespawner.minexptime = minexptime;
event.add_particlespawner.maxexptime = maxexptime;
event.add_particlespawner.minsize = minsize;
event.add_particlespawner.maxsize = maxsize;
event.add_particlespawner.collisiondetection = collisiondetection;
event.add_particlespawner.texture = new std::string(texture);
event.add_particlespawner.id = id;
m_client_event_queue.push_back(event);
}
else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u32 id = readU16(is);
ClientEvent event;
event.type = CE_DELETE_PARTICLESPAWNER;
event.delete_particlespawner.id = id;
m_client_event_queue.push_back(event);
}
else
{
infostream<<"Client: Ignoring unknown command "

View File

@ -157,7 +157,10 @@ enum ClientEventType
CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN,
CE_TEXTURES_UPDATED,
CE_SHOW_FORMSPEC
CE_SHOW_FORMSPEC,
CE_SPAWN_PARTICLE,
CE_ADD_PARTICLESPAWNER,
CE_DELETE_PARTICLESPAWNER
};
struct ClientEvent
@ -185,6 +188,35 @@ struct ClientEvent
} show_formspec;
struct{
} textures_updated;
struct{
v3f *pos;
v3f *vel;
v3f *acc;
f32 expirationtime;
f32 size;
bool collisiondetection;
std::string *texture;
} spawn_particle;
struct{
u16 amount;
f32 spawntime;
v3f *minpos;
v3f *maxpos;
v3f *minvel;
v3f *maxvel;
v3f *minacc;
v3f *maxacc;
f32 minexptime;
f32 maxexptime;
f32 minsize;
f32 maxsize;
bool collisiondetection;
std::string *texture;
u32 id;
} add_particlespawner;
struct{
u32 id;
} delete_particlespawner;
};
};

View File

@ -79,9 +79,15 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
Serialization format changes
PROTOCOL_VERSION 16:
TOCLIENT_SHOW_FORMSPEC
PROTOCOL_VERSION 17:
Serialization format change: include backface_culling flag in TileDef
Added rightclickable field in nodedef
TOCLIENT_SPAWN_PARTICLE
TOCLIENT_ADD_PARTICLESPAWNER
TOCLIENT_DELETE_PARTICLESPAWNER
*/
#define LATEST_PROTOCOL_VERSION 16
#define LATEST_PROTOCOL_VERSION 17
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13
@ -356,6 +362,7 @@ enum ToClientCommand
u8[len] name
[2] serialized inventory
*/
TOCLIENT_SHOW_FORMSPEC = 0x44,
/*
[0] u16 command
@ -381,6 +388,46 @@ enum ToClientCommand
f1000 movement_liquid_sink
f1000 movement_gravity
*/
TOCLIENT_SPAWN_PARTICLE = 0x46,
/*
u16 command
v3f1000 pos
v3f1000 velocity
v3f1000 acceleration
f1000 expirationtime
f1000 size
u8 bool collisiondetection
u32 len
u8[len] texture
*/
TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
/*
u16 command
u16 amount
f1000 spawntime
v3f1000 minpos
v3f1000 maxpos
v3f1000 minvel
v3f1000 maxvel
v3f1000 minacc
v3f1000 maxacc
f1000 minexptime
f1000 maxexptime
f1000 minsize
f1000 maxsize
u8 bool collisiondetection
u32 len
u8[len] texture
u32 id
*/
TOCLIENT_DELETE_PARTICLESPAWNER = 0x48,
/*
u16 command
u32 id
*/
};
enum ToServerCommand

View File

@ -42,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// (compatible with ContentFeatures). If you specified 0,0,1,1
// for each face, that would be the same as passing NULL.
void makeCuboid(MeshCollector *collector, const aabb3f &box,
const TileSpec *tiles, int tilecount,
TileSpec *tiles, int tilecount,
video::SColor &c, const f32* txc)
{
assert(tilecount >= 1 && tilecount <= 6);
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
if(txc == NULL)
{
static const f32 txc_default[24] = {
@ -97,15 +99,70 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
};
for(s32 j=0; j<24; j++)
v2f t;
for(int i = 0; i < tilecount; i++)
{
switch (tiles[i].rotation)
{
case 0:
break;
case 1: //R90
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
break;
case 2: //R180
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
break;
case 3: //R270
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
break;
case 4: //FXR90
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
tiles[i].texture.size.Y *= -1;
break;
case 5: //FXR270
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
t=vertices[i*4].TCoords;
tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
tiles[i].texture.size.Y *= -1;
break;
case 6: //FYR90
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
tiles[i].texture.pos.X += tiles[i].texture.size.X;
tiles[i].texture.size.X *= -1;
break;
case 7: //FYR270
for (int x = 0; x < 4; x++)
vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
tiles[i].texture.pos.X += tiles[i].texture.size.X;
tiles[i].texture.size.X *= -1;
break;
case 8: //FX
tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
tiles[i].texture.size.Y *= -1;
break;
case 9: //FY
tiles[i].texture.pos.X += tiles[i].texture.size.X;
tiles[i].texture.size.X *= -1;
break;
default:
break;
}
}
for(s32 j=0; j<24; j++)
{
int tileindex = MYMIN(j/4, tilecount-1);
vertices[j].TCoords *= tiles[tileindex].texture.size;
vertices[j].TCoords += tiles[tileindex].texture.pos;
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
for(s32 j=0; j<24; j+=4)
{
@ -1154,14 +1211,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3s16(0, 0, 1),
v3s16(0, 0, -1)
};
TileSpec tiles[6];
for(int i = 0; i < 6; i++)
{
// Handles facedir rotation for textures
tiles[i] = getNodeTile(n, p, tile_dirs[i], data);
}
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
@ -1172,17 +1223,43 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
i = boxes.begin();
i != boxes.end(); i++)
{
for(int j = 0; j < 6; j++)
{
// Handles facedir rotation for textures
tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
}
aabb3f box = *i;
box.MinEdge += pos;
box.MaxEdge += pos;
f32 temp;
if (box.MinEdge.X > box.MaxEdge.X)
{
temp=box.MinEdge.X;
box.MinEdge.X=box.MaxEdge.X;
box.MaxEdge.X=temp;
}
if (box.MinEdge.Y > box.MaxEdge.Y)
{
temp=box.MinEdge.Y;
box.MinEdge.Y=box.MaxEdge.Y;
box.MaxEdge.Y=temp;
}
if (box.MinEdge.Z > box.MaxEdge.Z)
{
temp=box.MinEdge.Z;
box.MinEdge.Z=box.MaxEdge.Z;
box.MaxEdge.Z=temp;
}
//
// Compute texture coords
f32 tx1 = (i->MinEdge.X/BS)+0.5;
f32 ty1 = (i->MinEdge.Y/BS)+0.5;
f32 tz1 = (i->MinEdge.Z/BS)+0.5;
f32 tx2 = (i->MaxEdge.X/BS)+0.5;
f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
f32 tx1 = (box.MinEdge.X/BS)+0.5;
f32 ty1 = (box.MinEdge.Y/BS)+0.5;
f32 tz1 = (box.MinEdge.Z/BS)+0.5;
f32 tx2 = (box.MaxEdge.X/BS)+0.5;
f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
f32 txc[24] = {
// up
tx1, 1-tz2, tx2, 1-tz1,
@ -1197,7 +1274,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// front
tx1, 1-ty2, tx2, 1-ty1,
};
makeCuboid(&collector, box, tiles, 6, c, txc);
}
break;}

View File

@ -2189,6 +2189,47 @@ void the_game(
{
update_wielded_item_trigger = true;
}
else if(event.type == CE_SPAWN_PARTICLE)
{
LocalPlayer* player = client.getEnv().getLocalPlayer();
AtlasPointer ap =
gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
new Particle(gamedef, smgr, player, client.getEnv(),
*event.spawn_particle.pos,
*event.spawn_particle.vel,
*event.spawn_particle.acc,
event.spawn_particle.expirationtime,
event.spawn_particle.size,
event.spawn_particle.collisiondetection, ap);
}
else if(event.type == CE_ADD_PARTICLESPAWNER)
{
LocalPlayer* player = client.getEnv().getLocalPlayer();
AtlasPointer ap =
gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
new ParticleSpawner(gamedef, smgr, player,
event.add_particlespawner.amount,
event.add_particlespawner.spawntime,
*event.add_particlespawner.minpos,
*event.add_particlespawner.maxpos,
*event.add_particlespawner.minvel,
*event.add_particlespawner.maxvel,
*event.add_particlespawner.minacc,
*event.add_particlespawner.maxacc,
event.add_particlespawner.minexptime,
event.add_particlespawner.maxexptime,
event.add_particlespawner.minsize,
event.add_particlespawner.maxsize,
event.add_particlespawner.collisiondetection,
ap,
event.add_particlespawner.id);
}
else if(event.type == CE_DELETE_PARTICLESPAWNER)
{
delete_particlespawner (event.delete_particlespawner.id);
}
}
}
@ -2418,7 +2459,8 @@ void the_game(
const ContentFeatures &features =
client.getNodeDefManager()->get(n);
addPunchingParticles
(gamedef, smgr, player, nodepos, features.tiles);
(gamedef, smgr, player, client.getEnv(),
nodepos, features.tiles);
}
}
@ -2456,7 +2498,8 @@ void the_game(
const ContentFeatures &features =
client.getNodeDefManager()->get(wasnode);
addDiggingParticles
(gamedef, smgr, player, nodepos, features.tiles);
(gamedef, smgr, player, client.getEnv(),
nodepos, features.tiles);
}
dig_time = 0;
@ -2737,6 +2780,7 @@ void the_game(
*/
allparticles_step(dtime, client.getEnv());
allparticlespawners_step(dtime, client.getEnv());
/*
Fog
@ -3222,6 +3266,7 @@ void the_game(
clouds->drop();
if(gui_chat_console)
gui_chat_console->drop();
clear_particles ();
/*
Draw a "shutting down" screen, which will be shown while the map

View File

@ -43,6 +43,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "subgame.h"
#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0]))
#define LSTRING(x) LSTRING_(x)
#define LSTRING_(x) L##x
const wchar_t *contrib_core_strs[] = {
L"Perttu Ahola (celeron55) <celeron55@gmail.com>",
@ -762,7 +764,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, 130, 70);
rect += m_topleft_client + v2s32(35, 160);
Environment->addStaticText(
L"Minetest " VERSION_STRING "\nhttp://minetest.net/",
L"Minetest " LSTRING(VERSION_STRING) L"\nhttp://minetest.net/",
rect, false, true, this, -1);
}
{

View File

@ -50,7 +50,7 @@ class NodeMetadata;
class IGameDef;
class IRollbackReportSink;
class EmergeManager;
class BlockMakeData;
struct BlockMakeData;
/*

View File

@ -455,6 +455,80 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
v3f vertex_pos[4];
v3s16 vertex_dirs[4];
getNodeVertexDirs(dir, vertex_dirs);
v3s16 t;
switch (tile.rotation)
{
case 0:
break;
case 1: //R90
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[3];
vertex_dirs[3] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[1];
vertex_dirs[1] = t;
break;
case 2: //R180
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[2];
vertex_dirs[2] = t;
t = vertex_dirs[1];
vertex_dirs[1] = vertex_dirs[3];
vertex_dirs[3] = t;
break;
case 3: //R270
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[1];
vertex_dirs[1] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[3];
vertex_dirs[3] = t;
break;
case 4: //FXR90
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[3];
vertex_dirs[3] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[1];
vertex_dirs[1] = t;
tile.texture.pos.Y += tile.texture.size.Y;
tile.texture.size.Y *= -1;
break;
case 5: //FXR270
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[1];
vertex_dirs[1] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[3];
vertex_dirs[3] = t;
tile.texture.pos.Y += tile.texture.size.Y;
tile.texture.size.Y *= -1;
break;
case 6: //FYR90
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[3];
vertex_dirs[3] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[1];
vertex_dirs[1] = t;
tile.texture.pos.X += tile.texture.size.X;
tile.texture.size.X *= -1;
break;
case 7: //FYR270
t = vertex_dirs[0];
vertex_dirs[0] = vertex_dirs[1];
vertex_dirs[1] = vertex_dirs[2];
vertex_dirs[2] = vertex_dirs[3];
vertex_dirs[3] = t;
tile.texture.pos.X += tile.texture.size.X;
tile.texture.size.X *= -1;
break;
case 8: //FX
tile.texture.pos.Y += tile.texture.size.Y;
tile.texture.size.Y *= -1;
break;
case 9: //FY
tile.texture.pos.X += tile.texture.size.X;
tile.texture.size.X *= -1;
break;
default:
break;
}
for(u16 i=0; i<4; i++)
{
vertex_pos[i] = v3f(
@ -601,60 +675,50 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
// 5 = (0,0,-1)
// 6 = (0,-1,0)
// 7 = (-1,0,0)
u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
// Get rotation for things like chests
u8 facedir = mn.getFaceDir(ndef);
assert(facedir <= 3);
static const u8 dir_to_tile[4 * 8] =
assert(facedir <= 23);
static const u16 dir_to_tile[24 * 16] =
{
// 0 +X +Y +Z 0 -Z -Y -X
0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
// 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 ,
0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 ,
0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
};
u8 tileindex = dir_to_tile[facedir*8 + dir_i];
// 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);
}
}
u16 tile_index=facedir*16 + dir_i;
TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
spec.rotation=dir_to_tile[tile_index + 1];
std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
spec.texture = data->m_gamedef->tsrc()->getTexture(name);
return spec;
}
@ -794,6 +858,7 @@ static void updateFastFaceRow(
&& next_lights[2] == lights[2]
&& next_lights[3] == lights[3]
&& next_tile == tile
&& tile.rotation == 0
&& next_light_source == light_source)
{
next_is_different = false;

View File

@ -45,7 +45,7 @@ class MapBlock;
class ManualMapVoxelManipulator;
class VoxelManipulator;
class INodeDefManager;
class BlockMakeData;
struct BlockMakeData;
class VoxelArea;
struct MapgenParams {

View File

@ -107,7 +107,7 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
if(f.param_type_2 == CPT2_FACEDIR)
return getParam2() & 0x03;
return getParam2() & 0x1F;
return 0;
}
@ -140,29 +140,131 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
{
const std::vector<aabb3f> &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr);
u8 axisdir = facedir>>2;
facedir&=0x03;
for(std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); i++)
{
aabb3f box = *i;
if(facedir == 1)
switch (axisdir)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
box.repair();
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
box.repair();
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
box.repair();
case 0:
if(facedir == 1)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
break;
case 1: // z+
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
break;
case 2: //z-
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
break;
case 3: //x+
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
break;
case 4: //x-
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
break;
case 5:
box.MinEdge.rotateXYBy(-180);
box.MaxEdge.rotateXYBy(-180);
if(facedir == 1)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
break;
default:
break;
}
box.repair();
boxes.push_back(box);
}
}

View File

@ -107,26 +107,31 @@ void NodeBox::deSerialize(std::istream &is)
TileDef
*/
void TileDef::serialize(std::ostream &os) const
void TileDef::serialize(std::ostream &os, u16 protocol_version) const
{
writeU8(os, 0); // version
if(protocol_version >= 17)
writeU8(os, 1);
else
writeU8(os, 0);
os<<serializeString(name);
writeU8(os, animation.type);
writeU16(os, animation.aspect_w);
writeU16(os, animation.aspect_h);
writeF1000(os, animation.length);
if(protocol_version >= 17)
writeU8(os, backface_culling);
}
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);
if(version >= 1)
backface_culling = readU8(is);
}
/*
@ -235,10 +240,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
writeF1000(os, visual_scale);
writeU8(os, 6);
for(u32 i=0; i<6; i++)
tiledef[i].serialize(os);
tiledef[i].serialize(os, protocol_version);
writeU8(os, CF_SPECIAL_COUNT);
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
tiledef_special[i].serialize(os);
tiledef_special[i].serialize(os, protocol_version);
}
writeU8(os, alpha);
writeU8(os, post_effect_color.getAlpha());
@ -270,9 +275,9 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
serializeSimpleSoundSpec(sound_footstep, os);
serializeSimpleSoundSpec(sound_dig, os);
serializeSimpleSoundSpec(sound_dug, os);
writeU8(os, rightclickable);
// Stuff below should be moved to correct place in a version that otherwise changes
// the protocol version
writeU8(os, rightclickable);
}
void ContentFeatures::deSerialize(std::istream &is)
@ -331,12 +336,12 @@ void ContentFeatures::deSerialize(std::istream &is)
deSerializeSimpleSoundSpec(sound_footstep, is);
deSerializeSimpleSoundSpec(sound_dig, is);
deSerializeSimpleSoundSpec(sound_dug, is);
rightclickable = readU8(is);
// If you add anything here, insert it primarily inside the try-catch
// block to not need to increase the version.
try{
// Stuff below should be moved to correct place in a version that
// otherwise changes the protocol version
rightclickable = readU8(is);
}catch(SerializationError &e) {};
}
@ -809,10 +814,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
writeF1000(os, visual_scale);
writeU8(os, 6);
for(u32 i=0; i<6; i++)
tiledef[i].serialize(os);
tiledef[i].serialize(os, protocol_version);
writeU8(os, CF_SPECIAL_COUNT);
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
tiledef_special[i].serialize(os);
tiledef_special[i].serialize(os, protocol_version);
}
writeU8(os, alpha);
writeU8(os, post_effect_color.getAlpha());

View File

@ -119,7 +119,7 @@ struct TileDef
animation.length = 1.0;
}
void serialize(std::ostream &os) const;
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
};

View File

@ -32,19 +32,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clientmap.h"
#include "mapnode.h"
/*
Utility
*/
v3f random_v3f(v3f min, v3f max)
{
return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
}
std::vector<Particle*> all_particles;
std::map<u32, ParticleSpawner*> all_particlespawners;
Particle::Particle(
IGameDef *gamedef,
scene::ISceneManager* smgr,
LocalPlayer *player,
s32 id,
ClientEnvironment &env,
v3f pos,
v3f velocity,
v3f acceleration,
float expirationtime,
float size,
bool collisiondetection,
AtlasPointer ap
):
scene::ISceneNode(smgr->getRootSceneNode(), smgr, id)
scene::ISceneNode(smgr->getRootSceneNode(), smgr)
{
// Misc
m_gamedef = gamedef;
@ -57,7 +72,6 @@ Particle::Particle(
m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
m_material.setTexture(0, ap.atlas);
m_ap = ap;
m_light = 0;
// Particle related
@ -68,10 +82,20 @@ Particle::Particle(
m_time = 0;
m_player = player;
m_size = size;
m_collisiondetection = collisiondetection;
// Irrlicht stuff (TODO)
m_collisionbox = core::aabbox3d<f32>(-size/2,-size/2,-size/2,size/2,size/2,size/2);
// Irrlicht stuff
m_collisionbox = core::aabbox3d<f32>
(-size/2,-size/2,-size/2,size/2,size/2,size/2);
this->setAutomaticCulling(scene::EAC_OFF);
// Init lighting
updateLight(env);
// Init model
updateVertices();
all_particles.push_back(this);
}
Particle::~Particle()
@ -82,8 +106,10 @@ void Particle::OnRegisterSceneNode()
{
if (IsVisible)
{
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
SceneManager->registerNodeForRendering
(this, scene::ESNRP_TRANSPARENT);
SceneManager->registerNodeForRendering
(this, scene::ESNRP_SOLID);
}
ISceneNode::OnRegisterSceneNode();
@ -96,45 +122,45 @@ void Particle::render()
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(m_material);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
video::SColor c(255, m_light, m_light, m_light);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x0(), m_ap.y1()),
video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y1()),
video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y0()),
video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, c ,m_ap.x0(), m_ap.y0()),
};
for(u16 i=0; i<4; i++)
{
vertices[i].Pos.rotateYZBy(m_player->getPitch());
vertices[i].Pos.rotateXZBy(m_player->getYaw());
m_box.addInternalPoint(vertices[i].Pos);
vertices[i].Pos += m_pos*BS;
}
u16 indices[] = {0,1,2, 2,3,0};
driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
driver->drawVertexPrimitiveList(m_vertices, 4,
indices, 2, video::EVT_STANDARD,
scene::EPT_TRIANGLES, video::EIT_16BIT);
}
void Particle::step(float dtime, ClientEnvironment &env)
{
core::aabbox3d<f32> box = m_collisionbox;
v3f p_pos = m_pos*BS;
v3f p_velocity = m_velocity*BS;
v3f p_acceleration = m_acceleration*BS;
collisionMoveSimple(&env.getClientMap(), m_gamedef,
BS*0.5, box,
0, dtime,
p_pos, p_velocity, p_acceleration);
m_pos = p_pos/BS;
m_velocity = p_velocity/BS;
m_acceleration = p_acceleration/BS;
m_time += dtime;
if (m_collisiondetection)
{
core::aabbox3d<f32> box = m_collisionbox;
v3f p_pos = m_pos*BS;
v3f p_velocity = m_velocity*BS;
v3f p_acceleration = m_acceleration*BS;
collisionMoveSimple(&env.getClientMap(), m_gamedef,
BS*0.5, box,
0, dtime,
p_pos, p_velocity, p_acceleration);
m_pos = p_pos/BS;
m_velocity = p_velocity/BS;
m_acceleration = p_acceleration/BS;
}
else
{
m_velocity += m_acceleration * dtime;
m_pos += m_velocity * dtime;
}
// Update lighting
updateLight(env);
// Update model
updateVertices();
}
void Particle::updateLight(ClientEnvironment &env)
{
u8 light = 0;
try{
v3s16 p = v3s16(
@ -151,11 +177,37 @@ void Particle::step(float dtime, ClientEnvironment &env)
m_light = decode_light(light);
}
std::vector<Particle*> all_particles;
void Particle::updateVertices()
{
video::SColor c(255, m_light, m_light, m_light);
m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
c, m_ap.x0(), m_ap.y1());
m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
c, m_ap.x1(), m_ap.y1());
m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
c, m_ap.x1(), m_ap.y0());
m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
c ,m_ap.x0(), m_ap.y0());
for(u16 i=0; i<4; i++)
{
m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
m_box.addInternalPoint(m_vertices[i].Pos);
m_vertices[i].Pos += m_pos*BS;
}
}
/*
Helpers
*/
void allparticles_step (float dtime, ClientEnvironment &env)
{
for(std::vector<Particle*>::iterator i = all_particles.begin(); i != all_particles.end();)
for(std::vector<Particle*>::iterator i = all_particles.begin();
i != all_particles.end();)
{
if ((*i)->get_expired())
{
@ -171,22 +223,28 @@ void allparticles_step (float dtime, ClientEnvironment &env)
}
}
void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
const TileSpec tiles[])
{
for (u16 j = 0; j < 32; j++) // set the amount of particles here
{
addNodeParticle(gamedef, smgr, player, pos, tiles);
addNodeParticle(gamedef, smgr, player, env, pos, tiles);
}
}
void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env,
v3s16 pos, const TileSpec tiles[])
{
addNodeParticle(gamedef, smgr, player, pos, tiles);
addNodeParticle(gamedef, smgr, player, env, pos, tiles);
}
// add a particle of a node
// used by digging and punching particles
void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
const TileSpec tiles[])
{
// Texture
u8 texid = myrand_range(0,5);
@ -205,7 +263,10 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer
ap.pos.Y = ap.y0() + (y1 - ap.y0()) * ((rand()%64)/64.-texsize);
// Physics
v3f velocity((rand()%100/50.-1)/1.5, rand()%100/35., (rand()%100/50.-1)/1.5);
v3f velocity( (rand()%100/50.-1)/1.5,
rand()%100/35.,
(rand()%100/50.-1)/1.5);
v3f acceleration(0,-9,0);
v3f particlepos = v3f(
(f32)pos.X+rand()%100/200.-0.25,
@ -213,17 +274,180 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer
(f32)pos.Z+rand()%100/200.-0.25
);
Particle *particle = new Particle(
new Particle(
gamedef,
smgr,
player,
0,
env,
particlepos,
velocity,
acceleration,
rand()%100/100., // expiration time
visual_size,
true,
ap);
all_particles.push_back(particle);
}
/*
ParticleSpawner
*/
ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
u16 amount, float time,
v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, AtlasPointer ap, u32 id)
{
m_gamedef = gamedef;
m_smgr = smgr;
m_player = player;
m_amount = amount;
m_spawntime = time;
m_minpos = minpos;
m_maxpos = maxpos;
m_minvel = minvel;
m_maxvel = maxvel;
m_minacc = minacc;
m_maxacc = maxacc;
m_minexptime = minexptime;
m_maxexptime = maxexptime;
m_minsize = minsize;
m_maxsize = maxsize;
m_collisiondetection = collisiondetection;
m_ap = ap;
m_time = 0;
for (u16 i = 0; i<=m_amount; i++)
{
float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
m_spawntimes.push_back(spawntime);
}
all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
}
ParticleSpawner::~ParticleSpawner() {}
void ParticleSpawner::step(float dtime, ClientEnvironment &env)
{
m_time += dtime;
if (m_spawntime != 0) // Spawner exists for a predefined timespan
{
for(std::vector<float>::iterator i = m_spawntimes.begin();
i != m_spawntimes.end();)
{
if ((*i) <= m_time && m_amount > 0)
{
m_amount--;
v3f pos = random_v3f(m_minpos, m_maxpos);
v3f vel = random_v3f(m_minvel, m_maxvel);
v3f acc = random_v3f(m_minacc, m_maxacc);
float exptime = rand()/(float)RAND_MAX
*(m_maxexptime-m_minexptime)
+m_minexptime;
float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize)
+m_minsize;
new Particle(
m_gamedef,
m_smgr,
m_player,
env,
pos,
vel,
acc,
exptime,
size,
m_collisiondetection,
m_ap);
m_spawntimes.erase(i);
}
else
{
i++;
}
}
}
else // Spawner exists for an infinity timespan, spawn on a per-second base
{
for (int i = 0; i <= m_amount; i++)
{
if (rand()/(float)RAND_MAX < dtime)
{
v3f pos = random_v3f(m_minpos, m_maxpos);
v3f vel = random_v3f(m_minvel, m_maxvel);
v3f acc = random_v3f(m_minacc, m_maxacc);
float exptime = rand()/(float)RAND_MAX
*(m_maxexptime-m_minexptime)
+m_minexptime;
float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize)
+m_minsize;
new Particle(
m_gamedef,
m_smgr,
m_player,
env,
pos,
vel,
acc,
exptime,
size,
m_collisiondetection,
m_ap);
}
}
}
}
void allparticlespawners_step (float dtime, ClientEnvironment &env)
{
for(std::map<u32, ParticleSpawner*>::iterator i =
all_particlespawners.begin();
i != all_particlespawners.end();)
{
if (i->second->get_expired())
{
delete i->second;
all_particlespawners.erase(i++);
}
else
{
i->second->step(dtime, env);
i++;
}
}
}
void delete_particlespawner (u32 id)
{
if (all_particlespawners.find(id) != all_particlespawners.end())
{
delete all_particlespawners.find(id)->second;
all_particlespawners.erase(id);
}
}
void clear_particles ()
{
for(std::map<u32, ParticleSpawner*>::iterator i =
all_particlespawners.begin();
i != all_particlespawners.end();)
{
delete i->second;
all_particlespawners.erase(i++);
}
for(std::vector<Particle*>::iterator i =
all_particles.begin();
i != all_particles.end();)
{
(*i)->remove();
delete *i;
all_particles.erase(i);
}
}

View File

@ -35,12 +35,13 @@ class Particle : public scene::ISceneNode
IGameDef* gamedef,
scene::ISceneManager* mgr,
LocalPlayer *player,
s32 id,
ClientEnvironment &env,
v3f pos,
v3f velocity,
v3f acceleration,
float expirationtime,
float size,
bool collisiondetection,
AtlasPointer texture
);
~Particle();
@ -69,6 +70,10 @@ class Particle : public scene::ISceneNode
{ return m_expiration < m_time; }
private:
void updateLight(ClientEnvironment &env);
void updateVertices();
video::S3DVertex m_vertices[4];
float m_time;
float m_expiration;
@ -87,12 +92,71 @@ private:
float m_size;
AtlasPointer m_ap;
u8 m_light;
bool m_collisiondetection;
};
class ParticleSpawner
{
public:
ParticleSpawner(IGameDef* gamedef,
scene::ISceneManager *smgr,
LocalPlayer *player,
u16 amount,
float time,
v3f minp, v3f maxp,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection,
AtlasPointer ap,
u32 id);
~ParticleSpawner();
void step(float dtime, ClientEnvironment &env);
bool get_expired ()
{ return (m_amount <= 0) && m_spawntime != 0; }
private:
float m_time;
IGameDef *m_gamedef;
scene::ISceneManager *m_smgr;
LocalPlayer *m_player;
u16 m_amount;
float m_spawntime;
v3f m_minpos;
v3f m_maxpos;
v3f m_minvel;
v3f m_maxvel;
v3f m_minacc;
v3f m_maxacc;
float m_minexptime;
float m_maxexptime;
float m_minsize;
float m_maxsize;
AtlasPointer m_ap;
std::vector<float> m_spawntimes;
bool m_collisiondetection;
};
void allparticles_step (float dtime, ClientEnvironment &env);
void allparticlespawners_step (float dtime, ClientEnvironment &env);
void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
void delete_particlespawner (u32 id);
void clear_particles ();
void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
const TileSpec tiles[]);
void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
const TileSpec tiles[]);
void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
const TileSpec tiles[]);
#endif

View File

@ -73,7 +73,7 @@ public:
else{
/* No add shall have been used */
assert(n->second != -2);
n->second = std::max(n->second, 0) + 1;
n->second = (std::max)(n->second, 0) + 1;
}
}
{

View File

@ -44,6 +44,7 @@ extern "C" {
#include "scriptapi_item.h"
#include "scriptapi_content.h"
#include "scriptapi_craft.h"
#include "scriptapi_particles.h"
/*****************************************************************************/
/* Mod related */
@ -1089,6 +1090,9 @@ static const struct luaL_Reg minetest_f [] = {
{"get_all_craft_recipes", l_get_all_craft_recipes},
{"rollback_get_last_node_actor", l_rollback_get_last_node_actor},
{"rollback_revert_actions_by", l_rollback_revert_actions_by},
{"add_particle", l_add_particle},
{"add_particlespawner", l_add_particlespawner},
{"delete_particlespawner", l_delete_particlespawner},
{NULL, NULL}
};

143
src/scriptapi_particles.cpp Normal file
View File

@ -0,0 +1,143 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "scriptapi.h"
#include "scriptapi_particles.h"
#include "server.h"
#include "script.h"
#include "scriptapi_types.h"
#include "scriptapi_common.h"
// add_particle(pos, velocity, acceleration, expirationtime,
// size, collisiondetection, texture, player)
// pos/velocity/acceleration = {x=num, y=num, z=num}
// expirationtime = num (seconds)
// size = num
// texture = e.g."default_wood.png"
int l_add_particle(lua_State *L)
{
// Get server from registry
Server *server = get_server(L);
// Get parameters
v3f pos = check_v3f(L, 1);
v3f vel = check_v3f(L, 2);
v3f acc = check_v3f(L, 3);
float expirationtime = luaL_checknumber(L, 4);
float size = luaL_checknumber(L, 5);
bool collisiondetection = lua_toboolean(L, 6);
std::string texture = luaL_checkstring(L, 7);
if (lua_gettop(L) == 8) // only spawn for a single player
{
const char *playername = luaL_checkstring(L, 8);
server->spawnParticle(playername,
pos, vel, acc, expirationtime,
size, collisiondetection, texture);
}
else // spawn for all players
{
server->spawnParticleAll(pos, vel, acc,
expirationtime, size, collisiondetection, texture);
}
return 1;
}
// add_particlespawner(amount, time,
// minpos, maxpos,
// minvel, maxvel,
// minacc, maxacc,
// minexptime, maxexptime,
// minsize, maxsize,
// collisiondetection,
// texture,
// player)
// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num}
// minexptime/maxexptime = num (seconds)
// minsize/maxsize = num
// collisiondetection = bool
// texture = e.g."default_wood.png"
int l_add_particlespawner(lua_State *L)
{
// Get server from registry
Server *server = get_server(L);
// Get parameters
u16 amount = luaL_checknumber(L, 1);
float time = luaL_checknumber(L, 2);
v3f minpos = check_v3f(L, 3);
v3f maxpos = check_v3f(L, 4);
v3f minvel = check_v3f(L, 5);
v3f maxvel = check_v3f(L, 6);
v3f minacc = check_v3f(L, 7);
v3f maxacc = check_v3f(L, 8);
float minexptime = luaL_checknumber(L, 9);
float maxexptime = luaL_checknumber(L, 10);
float minsize = luaL_checknumber(L, 11);
float maxsize = luaL_checknumber(L, 12);
bool collisiondetection = lua_toboolean(L, 13);
std::string texture = luaL_checkstring(L, 14);
if (lua_gettop(L) == 15) // only spawn for a single player
{
const char *playername = luaL_checkstring(L, 15);
u32 id = server->addParticleSpawner(playername,
amount, time,
minpos, maxpos,
minvel, maxvel,
minacc, maxacc,
minexptime, maxexptime,
minsize, maxsize,
collisiondetection,
texture);
lua_pushnumber(L, id);
}
else // spawn for all players
{
u32 id = server->addParticleSpawnerAll( amount, time,
minpos, maxpos,
minvel, maxvel,
minacc, maxacc,
minexptime, maxexptime,
minsize, maxsize,
collisiondetection,
texture);
lua_pushnumber(L, id);
}
return 1;
}
// delete_particlespawner(id, player)
// player (string) is optional
int l_delete_particlespawner(lua_State *L)
{
// Get server from registry
Server *server = get_server(L);
// Get parameters
u32 id = luaL_checknumber(L, 1);
if (lua_gettop(L) == 2) // only delete for one player
{
const char *playername = luaL_checkstring(L, 2);
server->deleteParticleSpawner(playername, id);
}
else // delete for all players
{
server->deleteParticleSpawnerAll(id);
}
return 1;
}

32
src/scriptapi_particles.h Normal file
View File

@ -0,0 +1,32 @@
/*
Minetest-c55
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef LUA_PARTICLES_H_
#define LUA_PARTICLES_H_
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
int l_add_particle(lua_State *L);
int l_add_particlespawner(lua_State *L);
int l_delete_particlespawner(lua_State *L);
#endif

View File

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
#include <iostream>
#include <queue>
#include <algorithm>
#include "clientserver.h"
#include "map.h"
#include "jmutexautolock.h"
@ -3474,6 +3475,132 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, co
m_con.Send(peer_id, 0, data, true);
}
// Spawns a particle on peer with peer_id
void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_SPAWN_PARTICLE);
writeV3F1000(os, pos);
writeV3F1000(os, velocity);
writeV3F1000(os, acceleration);
writeF1000(os, expirationtime);
writeF1000(os, size);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
// Spawns a particle on all peers
void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
}
// Adds a ParticleSpawner on peer with peer_id
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,
float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
writeU16(os, amount);
writeF1000(os, spawntime);
writeV3F1000(os, minpos);
writeV3F1000(os, maxpos);
writeV3F1000(os, minvel);
writeV3F1000(os, maxvel);
writeV3F1000(os, minacc);
writeV3F1000(os, maxacc);
writeF1000(os, minexptime);
writeF1000(os, maxexptime);
writeF1000(os, minsize);
writeF1000(os, maxsize);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
writeU32(os, id);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
// Adds a ParticleSpawner on all peers
void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendAddParticleSpawner(client->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
}
}
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
writeU16(os, id);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendDeleteParticleSpawnerAll(u32 id)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendDeleteParticleSpawner(client->peer_id, id);
}
}
void Server::BroadcastChatMessage(const std::wstring &message)
{
for(std::map<u16, RemoteClient*>::iterator
@ -4432,6 +4559,111 @@ void Server::notifyPlayers(const std::wstring msg)
BroadcastChatMessage(msg);
}
void Server::spawnParticle(const char *playername, v3f pos,
v3f velocity, v3f acceleration,
float expirationtime, float size, bool
collisiondetection, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return;
SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture)
{
SendSpawnParticleAll(pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
u32 Server::addParticleSpawner(const char *playername,
u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return -1;
u32 id = 0;
for(;;) // look for unused particlespawner id
{
id++;
if (std::find(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id)
== m_particlespawner_ids.end())
{
m_particlespawner_ids.push_back(id);
break;
}
}
SendAddParticleSpawner(player->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
collisiondetection, texture, id);
return id;
}
u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture)
{
u32 id = 0;
for(;;) // look for unused particlespawner id
{
id++;
if (std::find(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id)
== m_particlespawner_ids.end())
{
m_particlespawner_ids.push_back(id);
break;
}
}
SendAddParticleSpawnerAll(amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
collisiondetection, texture, id);
return id;
}
void Server::deleteParticleSpawner(const char *playername, u32 id)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return;
m_particlespawner_ids.erase(
std::remove(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id),
m_particlespawner_ids.end());
SendDeleteParticleSpawner(player->peer_id, id);
}
void Server::deleteParticleSpawnerAll(u32 id)
{
m_particlespawner_ids.erase(
std::remove(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id),
m_particlespawner_ids.end());
SendDeleteParticleSpawnerAll(id);
}
void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
{
m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);

View File

@ -456,6 +456,35 @@ public:
// Envlock and conlock should be locked when calling this
void notifyPlayer(const char *name, const std::wstring msg);
void notifyPlayers(const std::wstring msg);
void spawnParticle(const char *playername,
v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture);
void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture);
u32 addParticleSpawner(const char *playername,
u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture);
u32 addParticleSpawnerAll(u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture);
void deleteParticleSpawner(const char *playername, u32 id);
void deleteParticleSpawnerAll(u32 id);
void queueBlockEmerge(v3s16 blockpos, bool allow_generate);
@ -574,6 +603,41 @@ private:
void sendDetachedInventoryToAll(const std::string &name);
void sendDetachedInventories(u16 peer_id);
// Adds a ParticleSpawner on peer with peer_id
void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture, u32 id);
// Adds a ParticleSpawner on all peers
void SendAddParticleSpawnerAll(u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture, u32 id);
// Deletes ParticleSpawner on a single client
void SendDeleteParticleSpawner(u16 peer_id, u32 id);
// Deletes ParticleSpawner on all clients
void SendDeleteParticleSpawnerAll(u32 id);
// Spawns particle on single client
void SendSpawnParticle(u16 peer_id,
v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture);
// Spawns particle on all clients
void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture);
/*
Something random
*/
@ -790,6 +854,11 @@ private:
*/
// key = name
std::map<std::string, Inventory*> m_detached_inventories;
/*
Particles
*/
std::vector<u32> m_particlespawner_ids;
};
/*

View File

@ -205,7 +205,8 @@ struct TileSpec
texture == other.texture &&
alpha == other.alpha &&
material_type == other.material_type &&
material_flags == other.material_flags
material_flags == other.material_flags &&
rotation == other.rotation
);
}
@ -264,6 +265,7 @@ struct TileSpec
// Animation parameters
u8 animation_frame_count;
u16 animation_frame_length_ms;
u8 rotation;
};
#endif