diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 335802d85737..d23630c6ba23 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -209,8 +209,9 @@ LOCAL_SRC_FILES := \ jni/src/util/string.cpp \ jni/src/util/timetaker.cpp \ jni/src/touchscreengui.cpp \ - jni/src/database-leveldb.cpp \ - jni/src/settings.cpp + jni/src/database-leveldb.cpp \ + jni/src/settings.cpp \ + jni/src/wieldmesh.cpp # lua api LOCAL_SRC_FILES += \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eeceb6358feb..b1fd26247b84 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -466,6 +466,7 @@ set(minetest_SRCS shader.cpp sky.cpp tile.cpp + wieldmesh.cpp ${minetest_SCRIPT_SRCS} ) list(SORT minetest_SRCS) diff --git a/src/camera.cpp b/src/camera.cpp index d961d45b9c14..0b4e4fd53536 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -23,12 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // for g_settings #include "map.h" #include "clientmap.h" // MapDrawControl -#include "mesh.h" #include "player.h" -#include "tile.h" #include #include "settings.h" -#include "itemdef.h" // For wield visualization +#include "wieldmesh.h" #include "noise.h" // easeCurve #include "gamedef.h" #include "sound.h" @@ -50,7 +48,6 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldmgr(NULL), m_wieldnode(NULL), - m_wieldlight(0), m_draw_control(draw_control), m_gamedef(gamedef), @@ -77,12 +74,9 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_digging_anim(0), m_digging_button(-1), - m_dummymesh(createCubeMesh(v3f(1,1,1))), m_wield_change_timer(0.125), - m_wield_mesh_next(NULL), - m_previous_playeritem(-1), - m_previous_itemname(""), + m_wield_item_next(), m_camera_mode(CAMERA_MODE_FIRST) { @@ -99,14 +93,15 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, // all other 3D scene nodes and before the GUI. m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); - m_wieldnode = m_wieldmgr->addMeshSceneNode(m_dummymesh, NULL); // need a dummy mesh + m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, true); + m_wieldnode->setItem(ItemStack(), m_gamedef); + m_wieldnode->drop(); // m_wieldmgr grabbed it + m_wieldlightnode = m_wieldmgr->addLightSceneNode(NULL, v3f(0.0, 50.0, 0.0)); } Camera::~Camera() { m_wieldmgr->drop(); - - delete m_dummymesh; } bool Camera::successfullyCreated(std::wstring& error_message) @@ -156,22 +151,10 @@ void Camera::step(f32 dtime) } bool was_under_zero = m_wield_change_timer < 0; - if(m_wield_change_timer < 0.125) - m_wield_change_timer += dtime; - if(m_wield_change_timer > 0.125) - m_wield_change_timer = 0.125; + m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); - if(m_wield_change_timer >= 0 && was_under_zero) - { - if(m_wield_mesh_next) - { - m_wieldnode->setMesh(m_wield_mesh_next); - m_wieldnode->setVisible(true); - } else { - m_wieldnode->setVisible(false); - } - m_wield_mesh_next = NULL; - } + if (m_wield_change_timer >= 0 && was_under_zero) + m_wieldnode->setItem(m_wield_item_next, m_gamedef); if (m_view_bobbing_state != 0) { @@ -445,10 +428,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, v3f wield_position = v3f(55, -35, 65); //v3f wield_rotation = v3f(-100, 120, -100); v3f wield_rotation = v3f(-100, 120, -100); - if(m_wield_change_timer < 0) - wield_position.Y -= 40 + m_wield_change_timer*320; - else - wield_position.Y -= 40 - m_wield_change_timer*320; + wield_position.Y += fabs(m_wield_change_timer)*320 - 40; if(m_digging_anim < 0.05 || m_digging_anim > 0.5) { f32 frac = 1.0; @@ -486,7 +466,12 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, } m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); - m_wieldlight = player->light; + + // Shine light upon the wield mesh + video::SColor black(255,0,0,0); + m_wieldmgr->setAmbientLight(player->light_color.getInterpolated(black, 0.7)); + m_wieldlightnode->getLightData().DiffuseColor = player->light_color.getInterpolated(black, 0.3); + m_wieldlightnode->setPosition(v3f(30+5*sin(2*player->getYaw()*M_PI/180), -50, 0)); // Render distance feedback loop updateViewingRange(frametime, busytime); @@ -658,48 +643,20 @@ void Camera::setDigging(s32 button) m_digging_button = button; } -void Camera::wield(const ItemStack &item, u16 playeritem) +void Camera::wield(const ItemStack &item) { - IItemDefManager *idef = m_gamedef->idef(); - std::string itemname = item.getDefinition(idef).name; - m_wield_mesh_next = idef->getWieldMesh(itemname, m_gamedef); - if(playeritem != m_previous_playeritem && - !(m_previous_itemname == "" && itemname == "")) - { - m_previous_playeritem = playeritem; - m_previous_itemname = itemname; - if(m_wield_change_timer >= 0.125) - m_wield_change_timer = -0.125; - else if(m_wield_change_timer > 0) - { + if (item.name != m_wield_item_next.name) { + m_wield_item_next = item; + if (m_wield_change_timer > 0) m_wield_change_timer = -m_wield_change_timer; - } - } else { - if(m_wield_mesh_next) { - m_wieldnode->setMesh(m_wield_mesh_next); - m_wieldnode->setVisible(true); - } else { - m_wieldnode->setVisible(false); - } - m_wield_mesh_next = NULL; - if(m_previous_itemname != itemname) - { - m_previous_itemname = itemname; - m_wield_change_timer = 0; - } - else - m_wield_change_timer = 0.125; + else if (m_wield_change_timer == 0) + m_wield_change_timer = -0.001; } } void Camera::drawWieldedTool(irr::core::matrix4* translation) { - // Set vertex colors of wield mesh according to light level - u8 li = m_wieldlight; - video::SColor color(255,li,li,li); - setMeshColor(m_wieldnode->getMesh(), color); - - // Clear Z buffer + // Clear Z buffer so that the wielded tool stay in front of world geometry m_wieldmgr->getVideoDriver()->clearZBuffer(); // Draw the wielded node (in a separate scene manager) @@ -707,7 +664,7 @@ void Camera::drawWieldedTool(irr::core::matrix4* translation) cam->setAspectRatio(m_cameranode->getAspectRatio()); cam->setFOV(72.0*M_PI/180.0); cam->setNearValue(0.1); - cam->setFarValue(100); + cam->setFarValue(1000); if (translation != NULL) { irr::core::matrix4 startMatrix = cam->getAbsoluteTransformation(); diff --git a/src/camera.h b/src/camera.h index 8831257cc8c2..996be79b27e8 100644 --- a/src/camera.h +++ b/src/camera.h @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class LocalPlayer; struct MapDrawControl; class IGameDef; +class WieldMeshSceneNode; enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT}; @@ -127,7 +128,7 @@ class Camera void setDigging(s32 button); // Replace the wielded item mesh - void wield(const ItemStack &item, u16 playeritem); + void wield(const ItemStack &item); // Draw the wielded tool. // This has to happen *after* the main scene is drawn. @@ -157,8 +158,8 @@ class Camera scene::ICameraSceneNode* m_cameranode; scene::ISceneManager* m_wieldmgr; - scene::IMeshSceneNode* m_wieldnode; - u8 m_wieldlight; + WieldMeshSceneNode* m_wieldnode; + scene::ILightSceneNode* m_wieldlightnode; // draw control MapDrawControl& m_draw_control; @@ -203,14 +204,9 @@ class Camera // If 1, right-click digging animation s32 m_digging_button; - //dummymesh for camera - irr::scene::IAnimatedMesh* m_dummymesh; - // Animation when changing wielded item f32 m_wield_change_timer; - scene::IMesh *m_wield_mesh_next; - u16 m_previous_playeritem; - std::string m_previous_itemname; + ItemStack m_wield_item_next; CameraMode m_camera_mode; }; diff --git a/src/clientobject.h b/src/clientobject.h index cae551abc574..24150628eaa2 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -41,6 +41,7 @@ class ITextureSource; class IGameDef; class LocalPlayer; struct ItemStack; +class WieldMeshSceneNode; class ClientActiveObject : public ActiveObject { @@ -58,8 +59,10 @@ class ClientActiveObject : public ActiveObject virtual bool getCollisionBox(aabb3f *toset){return false;} virtual bool collideWithObjects(){return false;} virtual v3f getPosition(){return v3f(0,0,0);} + virtual scene::ISceneNode *getSceneNode(){return NULL;} virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;} virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;} + virtual WieldMeshSceneNode *getWieldMeshSceneNode(){return NULL;} virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;} virtual bool isPlayer() const {return false;} virtual bool isLocalPlayer() const {return false;} diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d1de23d2a492..8471b7e81408 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -45,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "main.h" // g_settings #include "camera.h" // CameraModes +#include "wieldmesh.h" #include "log.h" class Settings; @@ -551,6 +552,7 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_meshnode(NULL), m_animated_meshnode(NULL), + m_wield_meshnode(NULL), m_spritenode(NULL), m_textnode(NULL), m_position(v3f(0,10*BS,0)), @@ -683,38 +685,47 @@ core::aabbox3d* GenericCAO::getSelectionBox() v3f GenericCAO::getPosition() { - if(getParent() != NULL) - { - if(m_meshnode) - return m_meshnode->getAbsolutePosition(); - if(m_animated_meshnode) - return m_animated_meshnode->getAbsolutePosition(); - if(m_spritenode) - return m_spritenode->getAbsolutePosition(); - return m_position; + if (getParent() != NULL) { + scene::ISceneNode *node = getSceneNode(); + if (node) + return node->getAbsolutePosition(); + else + return m_position; } return pos_translator.vect_show; } -scene::IMeshSceneNode* GenericCAO::getMeshSceneNode() +scene::ISceneNode* GenericCAO::getSceneNode() { - if(m_meshnode) + if (m_meshnode) return m_meshnode; + if (m_animated_meshnode) + return m_animated_meshnode; + if (m_wield_meshnode) + return m_wield_meshnode; + if (m_spritenode) + return m_spritenode; return NULL; } +scene::IMeshSceneNode* GenericCAO::getMeshSceneNode() +{ + return m_meshnode; +} + scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode() { - if(m_animated_meshnode) - return m_animated_meshnode; - return NULL; + return m_animated_meshnode; +} + +WieldMeshSceneNode* GenericCAO::getWieldMeshSceneNode() +{ + return m_wield_meshnode; } scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode() { - if(m_spritenode) - return m_spritenode; - return NULL; + return m_spritenode; } void GenericCAO::setAttachments() @@ -769,6 +780,12 @@ void GenericCAO::removeFromScene(bool permanent) m_animated_meshnode->drop(); m_animated_meshnode = NULL; } + if(m_wield_meshnode) + { + m_wield_meshnode->remove(); + m_wield_meshnode->drop(); + m_wield_meshnode = NULL; + } if(m_spritenode) { m_spritenode->remove(); @@ -789,7 +806,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, m_smgr = smgr; m_irr = irr; - if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL) + if (getSceneNode() != NULL) return; m_visuals_expired = false; @@ -918,28 +935,23 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, errorstream<<"GenericCAO::addToScene(): Could not load mesh "<= 1){ infostream<<"textures[0]: "<idef(); ItemStack item(m_prop.textures[0], 1, 0, "", idef); - scene::IMesh *item_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef); - - // Copy mesh to be able to set unique vertex colors - scene::IMeshManipulator *manip = - irr->getVideoDriver()->getMeshManipulator(); - scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh); - m_meshnode = smgr->addMeshSceneNode(mesh, NULL); - m_meshnode->grab(); - mesh->drop(); + m_wield_meshnode = new WieldMeshSceneNode( + smgr->getRootSceneNode(), smgr, -1); + m_wield_meshnode->setItem(item, m_gamedef); + m_wield_meshnode->grab(); - m_meshnode->setScale(v3f(m_prop.visual_size.X/2, + m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, m_prop.visual_size.Y/2, m_prop.visual_size.X/2)); u8 li = m_last_light; - setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li)); + m_wield_meshnode->setColor(video::SColor(255,li,li,li)); } } else { infostream<<"GenericCAO::addToScene(): \""<getGUIEnvironment(); std::wstring wname = narrow_to_wide(m_name); @@ -981,6 +987,8 @@ void GenericCAO::updateLight(u8 light_at_pos) setMeshColor(m_meshnode->getMesh(), color); if(m_animated_meshnode) setMeshColor(m_animated_meshnode->getMesh(), color); + if(m_wield_meshnode) + m_wield_meshnode->setColor(color); if(m_spritenode) m_spritenode->setColor(color); } @@ -993,27 +1001,19 @@ v3s16 GenericCAO::getLightPosition() void GenericCAO::updateNodePos() { - if(getParent() != NULL) + if (getParent() != NULL) return; - v3s16 camera_offset = m_env->getCameraOffset(); - if(m_meshnode) - { - m_meshnode->setPosition(pos_translator.vect_show-intToFloat(camera_offset, BS)); - v3f rot = m_meshnode->getRotation(); - rot.Y = -m_yaw; - m_meshnode->setRotation(rot); - } - if(m_animated_meshnode) - { - m_animated_meshnode->setPosition(pos_translator.vect_show-intToFloat(camera_offset, BS)); - v3f rot = m_animated_meshnode->getRotation(); - rot.Y = -m_yaw; - m_animated_meshnode->setRotation(rot); - } - if(m_spritenode) - { - m_spritenode->setPosition(pos_translator.vect_show-intToFloat(camera_offset, BS)); + scene::ISceneNode *node = getSceneNode(); + + if (node) { + v3s16 camera_offset = m_env->getCameraOffset(); + node->setPosition(pos_translator.vect_show - intToFloat(camera_offset, BS)); + if (node != m_spritenode) { // rotate if not a sprite + v3f rot = node->getRotation(); + rot.Y = -m_yaw; + node->setRotation(rot); + } } } @@ -1107,20 +1107,10 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) continue; } ClientActiveObject *obj = m_env->getActiveObject(*ci); - if(obj) - { - scene::IMeshSceneNode *m_child_meshnode - = obj->getMeshSceneNode(); - scene::IAnimatedMeshSceneNode *m_child_animated_meshnode - = obj->getAnimatedMeshSceneNode(); - scene::IBillboardSceneNode *m_child_spritenode - = obj->getSpriteSceneNode(); - if(m_child_meshnode) - m_child_meshnode->setParent(m_smgr->getRootSceneNode()); - if(m_child_animated_meshnode) - m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode()); - if(m_child_spritenode) - m_child_spritenode->setParent(m_smgr->getRootSceneNode()); + if (obj) { + scene::ISceneNode *child_node = obj->getSceneNode(); + if (child_node) + child_node->setParent(m_smgr->getRootSceneNode()); } ++ci; } @@ -1132,22 +1122,17 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) for(std::vector::iterator ci = m_children.begin(); ci != m_children.end(); ci++) { - // Get the object of the child - ClientActiveObject *obj = m_env->getActiveObject(*ci); - if(obj) - obj->setAttachments(); + // Get the object of the child + ClientActiveObject *obj = m_env->getActiveObject(*ci); + if (obj) + obj->setAttachments(); } } // Make sure m_is_visible is always applied - if(m_meshnode) - m_meshnode->setVisible(m_is_visible); - if(m_animated_meshnode) - m_animated_meshnode->setVisible(m_is_visible); - if(m_spritenode) - m_spritenode->setVisible(m_is_visible); - if(m_textnode) - m_textnode->setVisible(m_is_visible); + scene::ISceneNode *node = getSceneNode(); + if (node) + node->setVisible(m_is_visible); if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht { @@ -1516,154 +1501,38 @@ void GenericCAO::updateAttachments() if(getParent() == NULL || m_attached_to_local) // Detach or don't attach { - if(m_meshnode) - { - v3f old_position = m_meshnode->getAbsolutePosition(); - v3f old_rotation = m_meshnode->getRotation(); - m_meshnode->setParent(m_smgr->getRootSceneNode()); - m_meshnode->setPosition(old_position); - m_meshnode->setRotation(old_rotation); - m_meshnode->updateAbsolutePosition(); + scene::ISceneNode *node = getSceneNode(); + if (node) { + v3f old_position = node->getAbsolutePosition(); + v3f old_rotation = node->getRotation(); + node->setParent(m_smgr->getRootSceneNode()); + node->setPosition(old_position); + node->setRotation(old_rotation); + node->updateAbsolutePosition(); } - if(m_animated_meshnode) - { - v3f old_position = m_animated_meshnode->getAbsolutePosition(); - v3f old_rotation = m_animated_meshnode->getRotation(); - m_animated_meshnode->setParent(m_smgr->getRootSceneNode()); - m_animated_meshnode->setPosition(old_position); - m_animated_meshnode->setRotation(old_rotation); - m_animated_meshnode->updateAbsolutePosition(); - } - if(m_spritenode) - { - v3f old_position = m_spritenode->getAbsolutePosition(); - v3f old_rotation = m_spritenode->getRotation(); - m_spritenode->setParent(m_smgr->getRootSceneNode()); - m_spritenode->setPosition(old_position); - m_spritenode->setRotation(old_rotation); - m_spritenode->updateAbsolutePosition(); - } - if(m_is_local_player) - { + if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); player->isAttached = false; } } else // Attach { - scene::IMeshSceneNode *parent_mesh = NULL; - if(getParent()->getMeshSceneNode()) - parent_mesh = getParent()->getMeshSceneNode(); - scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL; - if(getParent()->getAnimatedMeshSceneNode()) - parent_animated_mesh = getParent()->getAnimatedMeshSceneNode(); - scene::IBillboardSceneNode *parent_sprite = NULL; - if(getParent()->getSpriteSceneNode()) - parent_sprite = getParent()->getSpriteSceneNode(); - - scene::IBoneSceneNode *parent_bone = NULL; - if(parent_animated_mesh && m_attachment_bone != "") - { - parent_bone = - parent_animated_mesh->getJointNode(m_attachment_bone.c_str()); - } - // The spaghetti code below makes sure attaching works if either the - // parent or child is a spritenode, meshnode, or animatedmeshnode - // TODO: Perhaps use polymorphism here to save code duplication - if(m_meshnode) - { - if(parent_bone) - { - m_meshnode->setParent(parent_bone); - m_meshnode->setPosition(m_attachment_position); - m_meshnode->setRotation(m_attachment_rotation); - m_meshnode->updateAbsolutePosition(); - } - else - { - if(parent_mesh) - { - m_meshnode->setParent(parent_mesh); - m_meshnode->setPosition(m_attachment_position); - m_meshnode->setRotation(m_attachment_rotation); - m_meshnode->updateAbsolutePosition(); - } - else if(parent_animated_mesh) { - m_meshnode->setParent(parent_animated_mesh); - m_meshnode->setPosition(m_attachment_position); - m_meshnode->setRotation(m_attachment_rotation); - m_meshnode->updateAbsolutePosition(); - } - else if(parent_sprite) { - m_meshnode->setParent(parent_sprite); - m_meshnode->setPosition(m_attachment_position); - m_meshnode->setRotation(m_attachment_rotation); - m_meshnode->updateAbsolutePosition(); - } - } - } - if(m_animated_meshnode) - { - if(parent_bone) - { - m_animated_meshnode->setParent(parent_bone); - m_animated_meshnode->setPosition(m_attachment_position); - m_animated_meshnode->setRotation(m_attachment_rotation); - m_animated_meshnode->updateAbsolutePosition(); - } - else - { - if(parent_mesh) - { - m_animated_meshnode->setParent(parent_mesh); - m_animated_meshnode->setPosition(m_attachment_position); - m_animated_meshnode->setRotation(m_attachment_rotation); - m_animated_meshnode->updateAbsolutePosition(); - } else if(parent_animated_mesh) { - m_animated_meshnode->setParent(parent_animated_mesh); - m_animated_meshnode->setPosition(m_attachment_position); - m_animated_meshnode->setRotation(m_attachment_rotation); - m_animated_meshnode->updateAbsolutePosition(); - } else if(parent_sprite) { - m_animated_meshnode->setParent(parent_sprite); - m_animated_meshnode->setPosition(m_attachment_position); - m_animated_meshnode->setRotation(m_attachment_rotation); - m_animated_meshnode->updateAbsolutePosition(); - } - } + scene::ISceneNode *my_node = getSceneNode(); + + scene::ISceneNode *parent_node = getParent()->getSceneNode(); + scene::IAnimatedMeshSceneNode *parent_animated_mesh_node = + getParent()->getAnimatedMeshSceneNode(); + if (parent_animated_mesh_node && m_attachment_bone != "") { + parent_node = parent_animated_mesh_node->getJointNode(m_attachment_bone.c_str()); } - if(m_spritenode) - { - if(parent_bone) - { - m_spritenode->setParent(parent_bone); - m_spritenode->setPosition(m_attachment_position); - m_spritenode->setRotation(m_attachment_rotation); - m_spritenode->updateAbsolutePosition(); - } else { - if(parent_mesh) - { - m_spritenode->setParent(parent_mesh); - m_spritenode->setPosition(m_attachment_position); - m_spritenode->setRotation(m_attachment_rotation); - m_spritenode->updateAbsolutePosition(); - } - else if(parent_animated_mesh) { - m_spritenode->setParent(parent_animated_mesh); - m_spritenode->setPosition(m_attachment_position); - m_spritenode->setRotation(m_attachment_rotation); - m_spritenode->updateAbsolutePosition(); - } - else if(parent_sprite) { - m_spritenode->setParent(parent_sprite); - m_spritenode->setPosition(m_attachment_position); - m_spritenode->setRotation(m_attachment_rotation); - m_spritenode->updateAbsolutePosition(); - } - } + + if (my_node && parent_node) { + my_node->setParent(parent_node); + my_node->setPosition(m_attachment_position); + my_node->setRotation(m_attachment_rotation); + my_node->updateAbsolutePosition(); } - if(m_is_local_player) - { + if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); player->isAttached = true; } diff --git a/src/content_cao.h b/src/content_cao.h index bf1ac5b3fa02..69e2e54a2645 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -70,6 +70,7 @@ class GenericCAO : public ClientActiveObject core::aabbox3d m_selection_box; scene::IMeshSceneNode *m_meshnode; scene::IAnimatedMeshSceneNode *m_animated_meshnode; + WieldMeshSceneNode *m_wield_meshnode; scene::IBillboardSceneNode *m_spritenode; scene::ITextSceneNode* m_textnode; v3f m_position; @@ -131,10 +132,14 @@ class GenericCAO : public ClientActiveObject v3f getPosition(); + scene::ISceneNode *getSceneNode(); + scene::IMeshSceneNode *getMeshSceneNode(); scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(); + WieldMeshSceneNode *getWieldMeshSceneNode(); + scene::IBillboardSceneNode *getSpriteSceneNode(); inline bool isPlayer() const diff --git a/src/environment.cpp b/src/environment.cpp index 01a7e38dcee5..6e5305b1e014 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "clientmap.h" #include "localplayer.h" +#include "mapblock_mesh.h" #include "event.h" #endif #include "daynightratio.h" @@ -2330,21 +2331,28 @@ void ClientEnvironment::step(float dtime) player->move(dtime, this, 100*BS); } - - // Update lighting on all players on client - float light = 1.0; - try{ - // Get node at head - v3s16 p = player->getLightPosition(); - MapNode n = m_map->getNode(p); - light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef()); - } - catch(InvalidPositionException &e){ - light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0); - } - player->light = light; } - + + // Update lighting on local player (used for wield item) + u32 day_night_ratio = getDayNightRatio(); + { + // Get node at head + + // On InvalidPositionException, use this as default + // (day: LIGHT_SUN, night: 0) + MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); + + try { + v3s16 p = lplayer->getLightPosition(); + node_at_lplayer = m_map->getNode(p); + } catch (InvalidPositionException &e) {} + + u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u8 day = light & 0xff; + u8 night = (light >> 8) & 0xff; + finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + } + /* Step active objects and update lighting of them */ @@ -2367,10 +2375,10 @@ void ClientEnvironment::step(float dtime) // Get node at head v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNode(p); - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); } catch(InvalidPositionException &e){ - light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + light = blend_light(day_night_ratio, LIGHT_SUN, 0); } obj->updateLight(light); } diff --git a/src/game.cpp b/src/game.cpp index 272abe783595..fee45e31d25a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3656,7 +3656,7 @@ void Game::updateFrame(std::vector &highlight_boxes, if (mlist && (client->getPlayerItem() < mlist->getSize())) { ItemStack item = mlist->getItem(client->getPlayerItem()); - camera->wield(item, client->getPlayerItem()); + camera->wield(item); } runData->update_wielded_item_trigger = false; } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index f976c3eb7ad7..ac67c5b27199 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "mapblock_mesh.h" #include "mesh.h" +#include "wieldmesh.h" #include "tile.h" #endif #include "log.h" @@ -330,104 +331,78 @@ class CItemDefManager: public IWritableItemDefManager ITextureSource *tsrc = gamedef->getTextureSource(); INodeDefManager *nodedef = gamedef->getNodeDefManager(); - IrrlichtDevice *device = tsrc->getDevice(); - video::IVideoDriver *driver = device->getVideoDriver(); - const ItemDefinition *def = &get(name); + const ItemDefinition &def = get(name); // Create new ClientCached cc = new ClientCached(); - bool need_node_mesh = false; - // Create an inventory texture cc->inventory_texture = NULL; - if(def->inventory_image != "") - { - cc->inventory_texture = tsrc->getTexture(def->inventory_image); - } - else if(def->type == ITEM_NODE) - { - need_node_mesh = true; - } + if(def.inventory_image != "") + cc->inventory_texture = tsrc->getTexture(def.inventory_image); + + // Additional processing for nodes: + // - Create a wield mesh if WieldMeshSceneNode can't render + // the node on its own. + // - If inventory_texture isn't set yet, create one using + // render-to-texture. + if (def.type == ITEM_NODE) { + // Get node properties + content_t id = nodedef->getId(name); + const ContentFeatures &f = nodedef->get(id); - // Create a wield mesh - assert(cc->wield_mesh == NULL); - if(def->type == ITEM_NODE && def->wield_image == "") - { - need_node_mesh = true; - } - else if(def->wield_image != "" || def->inventory_image != "") - { - // Extrude the wield image into a mesh - - std::string imagename; - if(def->wield_image != "") - imagename = def->wield_image; - else - imagename = def->inventory_image; - - cc->wield_mesh = createExtrudedMesh( - tsrc->getTexture(imagename), - driver, - def->wield_scale * v3f(40.0, 40.0, 4.0)); - if(cc->wield_mesh == NULL) - { - infostream<<"ItemDefManager: WARNING: " - <<"updateTexturesAndMeshes(): " - <<"Unable to create extruded mesh for item " - <name<inventory_texture == NULL; - if(need_node_mesh) - { - /* - Get node properties - */ - content_t id = nodedef->getId(def->name); - const ContentFeatures &f = nodedef->get(id); + // Keep this in sync with WieldMeshSceneNode::setItem() + bool need_wield_mesh = + !(f.mesh_ptr[0] || + f.drawtype == NDT_NORMAL || + f.drawtype == NDT_ALLFACES || + f.drawtype == NDT_AIRLIKE); - u8 param1 = 0; - if(f.param_type == CPT_LIGHT) - param1 = 0xee; + scene::IMesh *node_mesh = NULL; - /* - Make a mesh from the node - */ bool reenable_shaders = false; - if(g_settings->getBool("enable_shaders")){ - reenable_shaders = true; - g_settings->setBool("enable_shaders",false); - } - MeshMakeData mesh_make_data(gamedef); - u8 param2 = 0; - if (f.param_type_2 == CPT2_WALLMOUNTED) - param2 = 1; - MapNode mesh_make_node(id, param1, param2); - mesh_make_data.fillSingleNode(&mesh_make_node); - MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); - scene::IMesh *node_mesh = mapblock_mesh.getMesh(); - assert(node_mesh); - video::SColor c(255, 255, 255, 255); - setMeshColor(node_mesh, c); - /* - Scale and translate the mesh so it's a unit cube - centered on the origin - */ - scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); - translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); + if (need_rtt_mesh || need_wield_mesh) { + u8 param1 = 0; + if (f.param_type == CPT_LIGHT) + param1 = 0xee; + + /* + Make a mesh from the node + */ + if (g_settings->getBool("enable_shaders")) { + reenable_shaders = true; + g_settings->setBool("enable_shaders", false); + } + MeshMakeData mesh_make_data(gamedef); + u8 param2 = 0; + if (f.param_type_2 == CPT2_WALLMOUNTED) + param2 = 1; + MapNode mesh_make_node(id, param1, param2); + mesh_make_data.fillSingleNode(&mesh_make_node); + MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); + node_mesh = mapblock_mesh.getMesh(); + node_mesh->grab(); + video::SColor c(255, 255, 255, 255); + setMeshColor(node_mesh, c); + + // scale and translate the mesh so it's a + // unit cube centered on the origin + scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); + translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); + } /* Draw node mesh into a render target texture */ - if(cc->inventory_texture == NULL) - { + if (need_rtt_mesh) { TextureFromMeshParams params; params.mesh = node_mesh; params.dim.set(64, 64); params.rtt_texture_name = "INVENTORY_" - + def->name + "_RTT"; + + def.name + "_RTT"; params.delete_texture_on_shutdown = true; params.camera_position.set(0, 1.0, -1.5); params.camera_position.rotateXZBy(45); @@ -449,8 +424,7 @@ class CItemDefManager: public IWritableItemDefManager tsrc->generateTextureFromMesh(params); // render-to-target didn't work - if(cc->inventory_texture == NULL) - { + if (cc->inventory_texture == NULL) { cc->inventory_texture = tsrc->getTexture(f.tiledef[0].name); } @@ -459,16 +433,16 @@ class CItemDefManager: public IWritableItemDefManager /* Use the node mesh as the wield mesh */ + if (need_wield_mesh) { + cc->wield_mesh = node_mesh; + cc->wield_mesh->grab(); - // Scale to proper wield mesh proportions - scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) - * def->wield_scale); - - cc->wield_mesh = node_mesh; - cc->wield_mesh->grab(); + // no way reference count can be smaller than 2 in this place! + assert(cc->wield_mesh->getReferenceCount() >= 2); + } - //no way reference count can be smaller than 2 in this place! - assert(cc->wield_mesh->getReferenceCount() >= 2); + if (node_mesh) + node_mesh->drop(); if (reenable_shaders) g_settings->setBool("enable_shaders",true); diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 84b7c11466b2..bdcf45beb2cd 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -48,6 +48,7 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name): last_animation(NO_ANIM), hotbar_image(""), hotbar_selected_image(""), + light_color(255,255,255,255), m_sneak_node(32767,32767,32767), m_sneak_node_exists(false), m_old_node_below(32767,32767,32767), diff --git a/src/localplayer.h b/src/localplayer.h index 16830f3ec873..16b66716da66 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -71,6 +71,8 @@ class LocalPlayer : public Player std::string hotbar_image; std::string hotbar_selected_image; + video::SColor light_color; + GenericCAO* getCAO() const { return m_cao; } diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 2459cf0d7e59..99a5ce0dd24b 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -320,7 +320,7 @@ u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data) Converts from day + night color values (0..255) and a given daynight_ratio to the final SColor shown on screen. */ -static void finalColorBlend(video::SColor& result, +void finalColorBlend(video::SColor& result, u8 day, u8 night, u32 daynight_ratio) { s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000; diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index e1cccc64e115..be56d4c586a0 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -195,6 +195,11 @@ u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef); u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef); u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data); +// Converts from day + night color values (0..255) +// and a given daynight_ratio to the final SColor shown on screen. +void finalColorBlend(video::SColor& result, + u8 day, u8 night, u32 daynight_ratio); + // Retrieves the TileSpec of a face of a node // Adds MATERIAL_FLAG_CRACK if the node is cracked TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data); diff --git a/src/mesh.cpp b/src/mesh.cpp index 19d75f9f5e7c..38b3d97bc44d 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -91,218 +91,6 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale) return anim_mesh; } -static scene::IAnimatedMesh* extrudeARGB(u32 twidth, u32 theight, u8 *data) -{ - const s32 argb_wstep = 4 * twidth; - const s32 alpha_threshold = 1; - - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - - // Front and back - { - video::S3DVertex vertices[8] = - { - video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1), - video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0), - video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0), - video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1), - video::S3DVertex(+0.5,-0.5,+0.5, 0,0,+1, c, 1,1), - video::S3DVertex(+0.5,+0.5,+0.5, 0,0,+1, c, 1,0), - video::S3DVertex(-0.5,+0.5,+0.5, 0,0,+1, c, 0,0), - video::S3DVertex(-0.5,-0.5,+0.5, 0,0,+1, c, 0,1), - }; - u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4}; - buf->append(vertices, 8, indices, 12); - } - - // "Interior" - // (add faces where a solid pixel is next to a transparent one) - u8 *solidity = new u8[(twidth+2) * (theight+2)]; - u32 wstep = twidth + 2; - for (u32 y = 0; y < theight + 2; ++y) - { - u8 *scanline = solidity + y * wstep; - if (y == 0 || y == theight + 1) - { - for (u32 x = 0; x < twidth + 2; ++x) - scanline[x] = 0; - } - else - { - scanline[0] = 0; - u8 *argb_scanline = data + (y - 1) * argb_wstep; - for (u32 x = 0; x < twidth; ++x) - scanline[x+1] = (argb_scanline[x*4+3] >= alpha_threshold); - scanline[twidth + 1] = 0; - } - } - - // without this, there would be occasional "holes" in the mesh - f32 eps = 0.01; - - for (u32 y = 0; y <= theight; ++y) - { - u8 *scanline = solidity + y * wstep + 1; - for (u32 x = 0; x <= twidth; ++x) - { - if (scanline[x] && !scanline[x + wstep]) - { - u32 xx = x + 1; - while (scanline[xx] && !scanline[xx + wstep]) - ++xx; - f32 vx1 = (x - eps) / (f32) twidth - 0.5; - f32 vx2 = (xx + eps) / (f32) twidth - 0.5; - f32 vy = 0.5 - (y - eps) / (f32) theight; - f32 tx1 = x / (f32) twidth; - f32 tx2 = xx / (f32) twidth; - f32 ty = (y - 0.5) / (f32) theight; - video::S3DVertex vertices[8] = - { - video::S3DVertex(vx1,vy,-0.5, 0,-1,0, c, tx1,ty), - video::S3DVertex(vx2,vy,-0.5, 0,-1,0, c, tx2,ty), - video::S3DVertex(vx2,vy,+0.5, 0,-1,0, c, tx2,ty), - video::S3DVertex(vx1,vy,+0.5, 0,-1,0, c, tx1,ty), - }; - u16 indices[6] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - x = xx - 1; - } - if (!scanline[x] && scanline[x + wstep]) - { - u32 xx = x + 1; - while (!scanline[xx] && scanline[xx + wstep]) - ++xx; - f32 vx1 = (x - eps) / (f32) twidth - 0.5; - f32 vx2 = (xx + eps) / (f32) twidth - 0.5; - f32 vy = 0.5 - (y + eps) / (f32) theight; - f32 tx1 = x / (f32) twidth; - f32 tx2 = xx / (f32) twidth; - f32 ty = (y + 0.5) / (f32) theight; - video::S3DVertex vertices[8] = - { - video::S3DVertex(vx1,vy,-0.5, 0,1,0, c, tx1,ty), - video::S3DVertex(vx1,vy,+0.5, 0,1,0, c, tx1,ty), - video::S3DVertex(vx2,vy,+0.5, 0,1,0, c, tx2,ty), - video::S3DVertex(vx2,vy,-0.5, 0,1,0, c, tx2,ty), - }; - u16 indices[6] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - x = xx - 1; - } - } - } - - for (u32 x = 0; x <= twidth; ++x) - { - u8 *scancol = solidity + x + wstep; - for (u32 y = 0; y <= theight; ++y) - { - if (scancol[y * wstep] && !scancol[y * wstep + 1]) - { - u32 yy = y + 1; - while (scancol[yy * wstep] && !scancol[yy * wstep + 1]) - ++yy; - f32 vx = (x - eps) / (f32) twidth - 0.5; - f32 vy1 = 0.5 - (y - eps) / (f32) theight; - f32 vy2 = 0.5 - (yy + eps) / (f32) theight; - f32 tx = (x - 0.5) / (f32) twidth; - f32 ty1 = y / (f32) theight; - f32 ty2 = yy / (f32) theight; - video::S3DVertex vertices[8] = - { - video::S3DVertex(vx,vy1,-0.5, 1,0,0, c, tx,ty1), - video::S3DVertex(vx,vy1,+0.5, 1,0,0, c, tx,ty1), - video::S3DVertex(vx,vy2,+0.5, 1,0,0, c, tx,ty2), - video::S3DVertex(vx,vy2,-0.5, 1,0,0, c, tx,ty2), - }; - u16 indices[6] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - y = yy - 1; - } - if (!scancol[y * wstep] && scancol[y * wstep + 1]) - { - u32 yy = y + 1; - while (!scancol[yy * wstep] && scancol[yy * wstep + 1]) - ++yy; - f32 vx = (x + eps) / (f32) twidth - 0.5; - f32 vy1 = 0.5 - (y - eps) / (f32) theight; - f32 vy2 = 0.5 - (yy + eps) / (f32) theight; - f32 tx = (x + 0.5) / (f32) twidth; - f32 ty1 = y / (f32) theight; - f32 ty2 = yy / (f32) theight; - video::S3DVertex vertices[8] = - { - video::S3DVertex(vx,vy1,-0.5, -1,0,0, c, tx,ty1), - video::S3DVertex(vx,vy2,-0.5, -1,0,0, c, tx,ty2), - video::S3DVertex(vx,vy2,+0.5, -1,0,0, c, tx,ty2), - video::S3DVertex(vx,vy1,+0.5, -1,0,0, c, tx,ty1), - }; - u16 indices[6] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - y = yy - 1; - } - } - } - - delete[] solidity; - - // Add to mesh - scene::SMesh *mesh = new scene::SMesh(); - mesh->addMeshBuffer(buf); - buf->drop(); - scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh); - mesh->drop(); - return anim_mesh; -} - -scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture, - video::IVideoDriver *driver, v3f scale) -{ - scene::IAnimatedMesh *mesh = NULL; - core::dimension2d size = texture->getOriginalSize(); - video::ECOLOR_FORMAT format = texture->getColorFormat(); - if (format == video::ECF_A8R8G8B8) - { - // Texture is in the correct color format, we can pass it - // to extrudeARGB right away. - void *data = texture->lock(MY_ETLM_READ_ONLY); - if (data == NULL) - return NULL; - mesh = extrudeARGB(size.Width, size.Height, (u8*) data); - texture->unlock(); - } - else - { - video::IImage *img1 = driver->createImageFromData(format, size, texture->lock(MY_ETLM_READ_ONLY)); - if (img1 == NULL) - return NULL; - - // img1 is in the texture's color format, convert to 8-bit ARGB - video::IImage *img2 = driver->createImage(video::ECF_A8R8G8B8, size); - if (img2 == NULL) - { - img1->drop(); - return NULL; - } - - img1->copyTo(img2); - img1->drop(); - mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock()); - img2->unlock(); - img2->drop(); - } - - // Set default material - mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture); - mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_LIGHTING, false); - mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - mesh->getMeshBuffer(0)->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - scaleMesh(mesh, scale); // also recalculates bounding box - return mesh; -} - void scaleMesh(scene::IMesh *mesh, v3f scale) { if(mesh == NULL) @@ -523,6 +311,8 @@ scene::IMesh* convertNodeboxNodeToMesh(ContentFeatures *f) for (u16 j = 0; j < 6; j++) { scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); dst_mesh->addMeshBuffer(buf); buf->drop(); } diff --git a/src/mesh.h b/src/mesh.h index 7539298cb8c1..29f5ec76ca6a 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "nodedef.h" -#include /* Create a new cube mesh. @@ -33,16 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ scene::IAnimatedMesh* createCubeMesh(v3f scale); -/* - Create a new extruded mesh from a texture. - Maximum bounding box is (+-scale.X/2, +-scale.Y/2, +-scale.Z). - Thickness is in Z direction. - - The resulting mesh has 1 material which must be defined by the caller. -*/ -scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture, - video::IVideoDriver *driver, v3f scale); - /* Multiplies each vertex coordinate by the specified scaling factors (componentwise vector multiplication). diff --git a/src/player.cpp b/src/player.cpp index 13866e5f5537..64c138d02682 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -39,7 +39,6 @@ Player::Player(IGameDef *gamedef, const char *name): is_climbing(false), swimming_vertical(false), camera_barely_in_ceiling(false), - light(0), inventory(gamedef->idef()), hp(PLAYER_MAX_HP), hurt_tilt_timer(0), diff --git a/src/player.h b/src/player.h index a5cc7123f146..435875233b45 100644 --- a/src/player.h +++ b/src/player.h @@ -249,8 +249,6 @@ class Player bool swimming_vertical; bool camera_barely_in_ceiling; - u8 light; - Inventory inventory; f32 movement_acceleration_default; diff --git a/src/test.cpp b/src/test.cpp index cd353c0ea473..6cd7983fc362 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -199,6 +199,16 @@ struct TestUtilities: public TestBase UASSERT(is_number("123") == true); UASSERT(is_number("") == false); UASSERT(is_number("123a") == false); + UASSERT(is_power_of_two(0) == false); + UASSERT(is_power_of_two(1) == true); + UASSERT(is_power_of_two(2) == true); + UASSERT(is_power_of_two(3) == false); + for (int exponent = 2; exponent <= 31; ++exponent) { + UASSERT(is_power_of_two((1 << exponent) - 1) == false); + UASSERT(is_power_of_two((1 << exponent)) == true); + UASSERT(is_power_of_two((1 << exponent) + 1) == false); + } + UASSERT(is_power_of_two((u32)-1) == false); } }; diff --git a/src/util/numeric.h b/src/util/numeric.h index ee8fea8cfb0e..e7fdc25999e8 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -361,5 +361,10 @@ inline float cycle_shift(float value, float by = 0, float max = 1) return value + by; } +inline bool is_power_of_two(u32 n) +{ + return n != 0 && (n & (n-1)) == 0; +} + #endif diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp new file mode 100644 index 000000000000..4552d357236b --- /dev/null +++ b/src/wieldmesh.cpp @@ -0,0 +1,380 @@ +/* +Minetest +Copyright (C) 2010-2014 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 "wieldmesh.h" +#include "inventory.h" +#include "gamedef.h" +#include "itemdef.h" +#include "nodedef.h" +#include "mesh.h" +#include "tile.h" +#include "log.h" +#include "util/numeric.h" +#include +#include + +#define WIELD_SCALE_FACTOR 30.0 +#define WIELD_SCALE_FACTOR_EXTRUDED 40.0 + +#define MIN_EXTRUSION_MESH_RESOLUTION 32 // not 16: causes too many "holes" +#define MAX_EXTRUSION_MESH_RESOLUTION 512 + +static scene::IMesh* createExtrusionMesh(int resolution_x, int resolution_y) +{ + const f32 r = 0.5; + + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + v3f scale(1.0, 1.0, 0.1); + + // Front and back + { + video::S3DVertex vertices[8] = { + // z- + video::S3DVertex(-r,+r,-r, 0,0,-1, c, 0,0), + video::S3DVertex(+r,+r,-r, 0,0,-1, c, 1,0), + video::S3DVertex(+r,-r,-r, 0,0,-1, c, 1,1), + video::S3DVertex(-r,-r,-r, 0,0,-1, c, 0,1), + // z+ + video::S3DVertex(-r,+r,+r, 0,0,+1, c, 0,0), + video::S3DVertex(-r,-r,+r, 0,0,+1, c, 0,1), + video::S3DVertex(+r,-r,+r, 0,0,+1, c, 1,1), + video::S3DVertex(+r,+r,+r, 0,0,+1, c, 1,0), + }; + u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4}; + buf->append(vertices, 8, indices, 12); + } + + f32 pixelsize_x = 1 / (f32) resolution_x; + f32 pixelsize_y = 1 / (f32) resolution_y; + + for (int i = 0; i < resolution_x; ++i) { + f32 pixelpos_x = i * pixelsize_x - 0.5; + f32 x0 = pixelpos_x; + f32 x1 = pixelpos_x + pixelsize_x; + f32 tex0 = (i + 0.1) * pixelsize_x; + f32 tex1 = (i + 0.9) * pixelsize_x; + video::S3DVertex vertices[8] = { + // x- + video::S3DVertex(x0,-r,-r, -1,0,0, c, tex0,1), + video::S3DVertex(x0,-r,+r, -1,0,0, c, tex1,1), + video::S3DVertex(x0,+r,+r, -1,0,0, c, tex1,0), + video::S3DVertex(x0,+r,-r, -1,0,0, c, tex0,0), + // x+ + video::S3DVertex(x1,-r,-r, +1,0,0, c, tex0,1), + video::S3DVertex(x1,+r,-r, +1,0,0, c, tex0,0), + video::S3DVertex(x1,+r,+r, +1,0,0, c, tex1,0), + video::S3DVertex(x1,-r,+r, +1,0,0, c, tex1,1), + }; + u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4}; + buf->append(vertices, 8, indices, 12); + } + for (int i = 0; i < resolution_y; ++i) { + f32 pixelpos_y = i * pixelsize_y - 0.5; + f32 y0 = -pixelpos_y - pixelsize_y; + f32 y1 = -pixelpos_y; + f32 tex0 = (i + 0.1) * pixelsize_y; + f32 tex1 = (i + 0.9) * pixelsize_y; + video::S3DVertex vertices[8] = { + // y- + video::S3DVertex(-r,y0,-r, 0,-1,0, c, 0,tex0), + video::S3DVertex(+r,y0,-r, 0,-1,0, c, 1,tex0), + video::S3DVertex(+r,y0,+r, 0,-1,0, c, 1,tex1), + video::S3DVertex(-r,y0,+r, 0,-1,0, c, 0,tex1), + // y+ + video::S3DVertex(-r,y1,-r, 0,+1,0, c, 0,tex0), + video::S3DVertex(-r,y1,+r, 0,+1,0, c, 0,tex1), + video::S3DVertex(+r,y1,+r, 0,+1,0, c, 1,tex1), + video::S3DVertex(+r,y1,-r, 0,+1,0, c, 1,tex0), + }; + u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4}; + buf->append(vertices, 8, indices, 12); + } + + // Define default material + video::SMaterial *material = &buf->getMaterial(); + material->MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material->BackfaceCulling = true; + material->setFlag(video::EMF_LIGHTING, false); + material->setFlag(video::EMF_BILINEAR_FILTER, false); + material->setFlag(video::EMF_TRILINEAR_FILTER, false); + // anisotropic filtering removes "thin black line" artifacts + material->setFlag(video::EMF_ANISOTROPIC_FILTER, true); + material->setFlag(video::EMF_TEXTURE_WRAP, false); + + // Create mesh object + scene::SMesh *mesh = new scene::SMesh(); + mesh->addMeshBuffer(buf); + buf->drop(); + scaleMesh(mesh, scale); // also recalculates bounding box + return mesh; +} + +/* + Caches extrusion meshes so that only one of them per resolution + is needed. Also caches one cube (for convenience). + + E.g. there is a single extrusion mesh that is used for all + 16x16 px images, another for all 256x256 px images, and so on. + + WARNING: Not thread safe. This should not be a problem since + rendering related classes (such as WieldMeshSceneNode) will be + used from the rendering thread only. +*/ +class ExtrusionMeshCache: public IReferenceCounted +{ +public: + // Constructor + ExtrusionMeshCache() + { + for (int resolution = MIN_EXTRUSION_MESH_RESOLUTION; + resolution <= MAX_EXTRUSION_MESH_RESOLUTION; + resolution *= 2) { + m_extrusion_meshes[resolution] = + createExtrusionMesh(resolution, resolution); + } + m_cube = createCubeMesh(v3f(1.0, 1.0, 1.0)); + } + // Destructor + virtual ~ExtrusionMeshCache() + { + for (std::map::iterator + it = m_extrusion_meshes.begin(); + it != m_extrusion_meshes.end(); ++it) { + it->second->drop(); + } + m_cube->drop(); + } + // Get closest extrusion mesh for given image dimensions + // Caller must drop the returned pointer + scene::IMesh* create(core::dimension2d dim) + { + // handle non-power of two textures inefficiently without cache + if (!is_power_of_two(dim.Width) || !is_power_of_two(dim.Height)) { + return createExtrusionMesh(dim.Width, dim.Height); + } + + int maxdim = MYMAX(dim.Width, dim.Height); + + std::map::iterator + it = m_extrusion_meshes.lower_bound(maxdim); + + if (it == m_extrusion_meshes.end()) { + // no viable resolution found; use largest one + it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION); + assert(it != m_extrusion_meshes.end()); + } + + scene::IMesh *mesh = it->second; + mesh->grab(); + return mesh; + } + // Returns a 1x1x1 cube mesh with one meshbuffer (material) per face + // Caller must drop the returned pointer + scene::IMesh* createCube() + { + m_cube->grab(); + return m_cube; + } + +private: + std::map m_extrusion_meshes; + scene::IMesh *m_cube; +}; + +ExtrusionMeshCache *g_extrusion_mesh_cache = NULL; + + +WieldMeshSceneNode::WieldMeshSceneNode( + scene::ISceneNode *parent, + scene::ISceneManager *mgr, + s32 id, + bool lighting +): + scene::ISceneNode(parent, mgr, id), + m_meshnode(NULL), + m_lighting(lighting), + m_bounding_box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +{ + // If this is the first wield mesh scene node, create a cache + // for extrusion meshes (and a cube mesh), otherwise reuse it + if (g_extrusion_mesh_cache == NULL) + g_extrusion_mesh_cache = new ExtrusionMeshCache(); + else + g_extrusion_mesh_cache->grab(); + + // Disable bounding box culling for this scene node + // since we won't calculate the bounding box. + setAutomaticCulling(scene::EAC_OFF); + + // Create the child scene node + scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube(); + m_meshnode = SceneManager->addMeshSceneNode(dummymesh, this, -1); + m_meshnode->setReadOnlyMaterials(false); + m_meshnode->setVisible(false); + dummymesh->drop(); // m_meshnode grabbed it +} + +WieldMeshSceneNode::~WieldMeshSceneNode() +{ + assert(g_extrusion_mesh_cache); + if (g_extrusion_mesh_cache->drop()) + g_extrusion_mesh_cache = NULL; +} + +void WieldMeshSceneNode::setCube(const TileSpec tiles[6], + v3f wield_scale, ITextureSource *tsrc) +{ + scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube(); + changeToMesh(cubemesh); + cubemesh->drop(); + + m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR); + + // Customize materials + for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) { + assert(i < 6); + video::SMaterial &material = m_meshnode->getMaterial(i); + material.setTexture(0, tiles[i].texture); + tiles[i].applyMaterialOptions(material); + } +} + +void WieldMeshSceneNode::setExtruded(const std::string &imagename, + v3f wield_scale, ITextureSource *tsrc) +{ + video::ITexture *texture = tsrc->getTexture(imagename); + if (!texture) { + changeToMesh(NULL); + return; + } + + scene::IMesh *mesh = g_extrusion_mesh_cache->create(texture->getSize()); + changeToMesh(mesh); + mesh->drop(); + + m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED); + + // Customize material + assert(m_meshnode->getMaterialCount() == 1); + video::SMaterial &material = m_meshnode->getMaterial(0); + material.setTexture(0, texture); +} + +void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) +{ + ITextureSource *tsrc = gamedef->getTextureSource(); + IItemDefManager *idef = gamedef->getItemDefManager(); + + const ItemDefinition &def = item.getDefinition(idef); + + // If wield_image is defined, it overrides everything else + if (def.wield_image != "") { + setExtruded(def.wield_image, def.wield_scale, tsrc); + return; + } + + // Handle nodes + // See also CItemDefManager::createClientCached() + if (def.type == ITEM_NODE) { + INodeDefManager *ndef = gamedef->getNodeDefManager(); + const ContentFeatures &f = ndef->get(def.name); + if (f.mesh_ptr[0]) { + // e.g. mesh nodes and nodeboxes + changeToMesh(f.mesh_ptr[0]); + // mesh_ptr[0] is pre-scaled by BS * f->visual_scale + m_meshnode->setScale( + def.wield_scale * WIELD_SCALE_FACTOR + / (BS * f.visual_scale)); + // Customize materials + for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) { + assert(i < 6); + video::SMaterial &material = m_meshnode->getMaterial(i); + material.setTexture(0, f.tiles[i].texture); + f.tiles[i].applyMaterialOptions(material); + } + return; + } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { + setCube(f.tiles, def.wield_scale, tsrc); + return; + } else if (f.drawtype == NDT_AIRLIKE) { + changeToMesh(NULL); + return; + } + + // If none of the above standard cases worked, use the wield mesh from ClientCached + scene::IMesh *mesh = idef->getWieldMesh(item.name, gamedef); + if (mesh) { + changeToMesh(mesh); + m_meshnode->setScale(def.wield_scale * WIELD_SCALE_FACTOR); + return; + } + } + + // default to inventory_image + if (def.inventory_image != "") { + setExtruded(def.inventory_image, def.wield_scale, tsrc); + return; + } + + // no wield mesh found + changeToMesh(NULL); +} + +void WieldMeshSceneNode::setColor(video::SColor color) +{ + assert(!m_lighting); + setMeshColor(m_meshnode->getMesh(), color); +} + +void WieldMeshSceneNode::render() +{ + // note: if this method is changed to actually do something, + // you probably should implement OnRegisterSceneNode as well +} + +void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) +{ + if (mesh == NULL) { + scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube(); + m_meshnode->setVisible(false); + m_meshnode->setMesh(dummymesh); + dummymesh->drop(); // m_meshnode grabbed it + } + + if (m_lighting) { + m_meshnode->setMesh(mesh); + } else { + /* + Lighting is disabled, this means the caller can (and probably will) + call setColor later. We therefore need to clone the mesh so that + setColor will only modify this scene node's mesh, not others'. + */ + scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator(); + scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh); + m_meshnode->setMesh(new_mesh); + new_mesh->drop(); // m_meshnode grabbed it + } + + m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting); + // need to normalize normals when lighting is enabled (because of setScale()) + m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting); + m_meshnode->setVisible(true); + +} diff --git a/src/wieldmesh.h b/src/wieldmesh.h new file mode 100644 index 000000000000..7761fd51b94e --- /dev/null +++ b/src/wieldmesh.h @@ -0,0 +1,71 @@ +/* +Minetest +Copyright (C) 2010-2014 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 WIELDMESH_HEADER +#define WIELDMESH_HEADER + +#include "irrlichttypes_extrabloated.h" +#include + +class ItemStack; +class IGameDef; +class ITextureSource; +struct TileSpec; + +/* + Wield item scene node, renders the wield mesh of some item +*/ +class WieldMeshSceneNode: public scene::ISceneNode +{ +public: + WieldMeshSceneNode(scene::ISceneNode *parent, scene::ISceneManager *mgr, + s32 id = -1, bool lighting = false); + virtual ~WieldMeshSceneNode(); + + void setCube(const TileSpec tiles[6], + v3f wield_scale, ITextureSource *tsrc); + void setExtruded(const std::string &imagename, + v3f wield_scale, ITextureSource *tsrc); + void setItem(const ItemStack &item, IGameDef *gamedef); + + // Sets the vertex color of the wield mesh. + // Must only be used if the constructor was called with lighting = false + void setColor(video::SColor color); + + virtual void render(); + + virtual const core::aabbox3d& getBoundingBox() const + { return m_bounding_box; } + +private: + void changeToMesh(scene::IMesh *mesh); + + // Child scene node with the current wield mesh + scene::IMeshSceneNode *m_meshnode; + + // True if EMF_LIGHTING should be enabled. + bool m_lighting; + + // Bounding box culling is disabled for this type of scene node, + // so this variable is just required so we can implement + // getBoundingBox() and is set to an empty box. + core::aabbox3d m_bounding_box; +}; + +#endif