diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e1bfd65d..24f682f3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -262,6 +262,7 @@ set(minetest_SRCS keycode.cpp camera.cpp clouds.cpp + particles.cpp clientobject.cpp chat.cpp guiMainMenu.cpp diff --git a/src/client.h b/src/client.h index 7052e840a..43fac9c9a 100644 --- a/src/client.h +++ b/src/client.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filecache.h" #include "localplayer.h" #include "server.h" +#include "particles.h" #include "util/pointedthing.h" struct MeshMakeData; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 9ada214ae..ca5f33609 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -124,6 +124,7 @@ void set_default_settings(Settings *settings) settings->setDefault("preload_item_visuals", "true"); settings->setDefault("enable_shaders", "2"); settings->setDefault("repeat_rightclick_time", "0.25"); + settings->setDefault("enable_particles", "true"); settings->setDefault("media_fetch_threads", "8"); diff --git a/src/game.cpp b/src/game.cpp index bed99837d..488d18f7a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiChatConsole.h" #include "config.h" #include "clouds.h" +#include "particles.h" #include "camera.h" #include "farmesh.h" #include "mapblock.h" @@ -2399,6 +2400,13 @@ void the_game( else { dig_time_complete = params.time; + if (g_settings->getBool("enable_particles")) + { + const ContentFeatures &features = + client.getNodeDefManager()->get(n); + addPunchingParticles + (gamedef, smgr, player, nodepos, features.tiles); + } } if(dig_time_complete >= 0.001) @@ -2430,6 +2438,14 @@ void the_game( MapNode wasnode = map.getNode(nodepos); client.removeNode(nodepos); + if (g_settings->getBool("enable_particles")) + { + const ContentFeatures &features = + client.getNodeDefManager()->get(wasnode); + addDiggingParticles + (gamedef, smgr, player, nodepos, features.tiles); + } + dig_time = 0; digging = false; @@ -2698,6 +2714,12 @@ void the_game( farmesh->update(v2f(player_position.X, player_position.Z), brightness, farmesh_range); } + + /* + Update particles + */ + + allparticles_step(dtime, client.getEnv()); /* Fog diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index d7c3f54ff..bac9052b9 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -104,6 +104,7 @@ enum GUI_ID_TRILINEAR_CB, GUI_ID_SHADERS_CB, GUI_ID_PRELOAD_ITEM_VISUALS_CB, + GUI_ID_ENABLE_PARTICLES_CB, GUI_ID_DAMAGE_CB, GUI_ID_CREATIVE_CB, GUI_ID_JOIN_GAME_BUTTON, @@ -618,7 +619,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) GUI_ID_TRILINEAR_CB, wgettext("Tri-Linear Filtering")); } - // shader/on demand image loading settings + // shader/on demand image loading/particles settings { core::rect rect(0, 0, option_w+20, 30); rect += m_topleft_client + v2s32(option_x+175*2, option_y); @@ -633,6 +634,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) GUI_ID_PRELOAD_ITEM_VISUALS_CB, wgettext("Preload item visuals")); } + { + core::rect rect(0, 0, option_w+20+20, 30); + rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*2); + Environment->addCheckBox(m_data->enable_particles, rect, this, + GUI_ID_ENABLE_PARTICLES_CB, wgettext("Enable Particles")); + } + // Key change button { core::rect rect(0, 0, 120, 30); @@ -849,6 +857,12 @@ void GUIMainMenu::readInput(MainMenuData *dst) dst->preload_item_visuals = ((gui::IGUICheckBox*)e)->isChecked(); } + { + gui::IGUIElement *e = getElementFromId(GUI_ID_ENABLE_PARTICLES_CB); + if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) + dst->enable_particles = ((gui::IGUICheckBox*)e)->isChecked(); + } + { gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX); if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX) diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h index 604202461..f87ad0fdb 100644 --- a/src/guiMainMenu.h +++ b/src/guiMainMenu.h @@ -47,6 +47,7 @@ struct MainMenuData bool trilinear_filter; int enable_shaders; bool preload_item_visuals; + bool enable_particles; // Server options bool creative_mode; bool enable_damage; diff --git a/src/main.cpp b/src/main.cpp index ede9f63b0..0af9d113c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1448,6 +1448,7 @@ int main(int argc, char *argv[]) menudata.trilinear_filter = g_settings->getBool("trilinear_filter"); menudata.enable_shaders = g_settings->getS32("enable_shaders"); menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals"); + menudata.enable_particles = g_settings->getBool("enable_particles"); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map); menudata.creative_mode = g_settings->getBool("creative_mode"); menudata.enable_damage = g_settings->getBool("enable_damage"); @@ -1570,6 +1571,7 @@ int main(int argc, char *argv[]) g_settings->setS32("enable_shaders", menudata.enable_shaders); g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals)); + g_settings->set("enable_particles", itos(menudata.enable_particles)); g_settings->set("creative_mode", itos(menudata.creative_mode)); g_settings->set("enable_damage", itos(menudata.enable_damage)); diff --git a/src/particles.cpp b/src/particles.cpp new file mode 100644 index 000000000..d49e33322 --- /dev/null +++ b/src/particles.cpp @@ -0,0 +1,229 @@ +/* +Minetest-c55 +Copyright (C) 2012 celeron55, Perttu Ahola + +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 "particles.h" +#include "constants.h" +#include "debug.h" +#include "main.h" // For g_profiler and g_settings +#include "settings.h" +#include "tile.h" +#include "gamedef.h" +#include "collision.h" +#include +#include "util/numeric.h" +#include "light.h" +#include "environment.h" +#include "clientmap.h" +#include "mapnode.h" + +Particle::Particle( + IGameDef *gamedef, + scene::ISceneManager* smgr, + LocalPlayer *player, + s32 id, + v3f pos, + v3f velocity, + v3f acceleration, + float expirationtime, + float size, + AtlasPointer ap +): + scene::ISceneNode(smgr->getRootSceneNode(), smgr, id) +{ + // Misc + m_gamedef = gamedef; + + // Texture + m_material.setFlag(video::EMF_LIGHTING, false); + m_material.setFlag(video::EMF_BACK_FACE_CULLING, false); + m_material.setFlag(video::EMF_BILINEAR_FILTER, false); + m_material.setFlag(video::EMF_FOG_ENABLE, true); + m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + m_material.setTexture(0, ap.atlas); + m_ap = ap; + m_light = 0; + + + // Particle related + m_pos = pos; + m_velocity = velocity; + m_acceleration = acceleration; + m_expiration = expirationtime; + m_time = 0; + m_player = player; + m_size = size; + + // Irrlicht stuff (TODO) + m_collisionbox = core::aabbox3d(-size/2,-size/2,-size/2,size/2,size/2,size/2); + this->setAutomaticCulling(scene::EAC_OFF); +} + +Particle::~Particle() +{ +} + +void Particle::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); + SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); + } + + ISceneNode::OnRegisterSceneNode(); +} + +void Particle::render() +{ + // TODO: Render particles in front of water and the selectionbox + + 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); +} + +void Particle::step(float dtime, ClientEnvironment &env) +{ + core::aabbox3d 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; + + // Update lighting + u8 light = 0; + try{ + v3s16 p = v3s16( + floor(m_pos.X+0.5), + floor(m_pos.Y+0.5), + floor(m_pos.Z+0.5) + ); + MapNode n = env.getClientMap().getNode(p); + light = n.getLightBlend(env.getDayNightRatio(), m_gamedef->ndef()); + } + catch(InvalidPositionException &e){ + light = blend_light(env.getDayNightRatio(), LIGHT_SUN, 0); + } + m_light = decode_light(light); +} + +std::vector all_particles; + +void allparticles_step (float dtime, ClientEnvironment &env) +{ + for(std::vector::iterator i = all_particles.begin(); i != all_particles.end();) + { + if ((*i)->get_expired()) + { + (*i)->remove(); + delete *i; + all_particles.erase(i); + } + else + { + (*i)->step(dtime, env); + i++; + } + } +} + +void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +{ + for (u16 j = 0; j < 32; j++) // set the amount of particles here + { + addNodeParticle(gamedef, smgr, player, pos, tiles); + } +} + +void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +{ + addNodeParticle(gamedef, smgr, player, 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[]) +{ + // Texture + u8 texid = myrand_range(0,5); + AtlasPointer ap = tiles[texid].texture; + float size = rand()%64/512.; + float visual_size = BS*size; + float texsize = size*2; + + float x1 = ap.x1(); + float y1 = ap.y1(); + + ap.size.X = (ap.x1() - ap.x0()) * texsize; + ap.size.Y = (ap.x1() - ap.x0()) * texsize; + + ap.pos.X = ap.x0() + (x1 - ap.x0()) * ((rand()%64)/64.-texsize); + 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 acceleration(0,-9,0); + v3f particlepos = v3f( + (f32)pos.X+rand()%100/200.-0.25, + (f32)pos.Y+rand()%100/200.-0.25, + (f32)pos.Z+rand()%100/200.-0.25 + ); + + Particle *particle = new Particle( + gamedef, + smgr, + player, + 0, + particlepos, + velocity, + acceleration, + rand()%100/100., // expiration time + visual_size, + ap); + + all_particles.push_back(particle); +} diff --git a/src/particles.h b/src/particles.h new file mode 100644 index 000000000..3ed9dfdc8 --- /dev/null +++ b/src/particles.h @@ -0,0 +1,98 @@ +/* +Minetest-c55 +Copyright (C) 2012 celeron55, Perttu Ahola + +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 PARTICLES_HEADER +#define PARTICLES_HEADER + +#define DIGGING_PARTICLES_AMOUNT 10 + +#include +#include "irrlichttypes_extrabloated.h" +#include "tile.h" +#include "localplayer.h" +#include "environment.h" + +class Particle : public scene::ISceneNode +{ + public: + Particle( + IGameDef* gamedef, + scene::ISceneManager* mgr, + LocalPlayer *player, + s32 id, + v3f pos, + v3f velocity, + v3f acceleration, + float expirationtime, + float size, + AtlasPointer texture + ); + ~Particle(); + + virtual const core::aabbox3d& getBoundingBox() const + { + return m_box; + } + + virtual u32 getMaterialCount() const + { + return 1; + } + + virtual video::SMaterial& getMaterial(u32 i) + { + return m_material; + } + + virtual void OnRegisterSceneNode(); + virtual void render(); + + void step(float dtime, ClientEnvironment &env); + + bool get_expired () + { return m_expiration < m_time; } + +private: + float m_time; + float m_expiration; + + IGameDef *m_gamedef; + core::aabbox3d m_box; + core::aabbox3d m_collisionbox; + video::SMaterial m_material; + v3f m_pos; + v3f m_velocity; + v3f m_acceleration; + float tex_x0; + float tex_x1; + float tex_y0; + float tex_y1; + LocalPlayer *m_player; + float m_size; + AtlasPointer m_ap; + u8 m_light; +}; + +void allparticles_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[]); + +#endif