Implement WieldMeshSceneNode which improves wield mesh rendering

- Don't create and cache an extruded mesh for every (non-node) item.
  Instead use a single one per image resolution.

- For cubic nodes reuse a single wield mesh too

- Improve lighting of the wielded item

- Increase far value of wield mesh scene camera, fixes #1770

- Also includes some minor refactorings of Camera and GenericCAO.
This commit is contained in:
Kahrl 2014-11-02 03:47:43 +01:00
parent cc8d7b8640
commit 9b551d5cbc
22 changed files with 696 additions and 632 deletions

@ -210,7 +210,8 @@ LOCAL_SRC_FILES := \
jni/src/util/timetaker.cpp \ jni/src/util/timetaker.cpp \
jni/src/touchscreengui.cpp \ jni/src/touchscreengui.cpp \
jni/src/database-leveldb.cpp \ jni/src/database-leveldb.cpp \
jni/src/settings.cpp jni/src/settings.cpp \
jni/src/wieldmesh.cpp
# lua api # lua api
LOCAL_SRC_FILES += \ LOCAL_SRC_FILES += \

@ -466,6 +466,7 @@ set(minetest_SRCS
shader.cpp shader.cpp
sky.cpp sky.cpp
tile.cpp tile.cpp
wieldmesh.cpp
${minetest_SCRIPT_SRCS} ${minetest_SCRIPT_SRCS}
) )
list(SORT minetest_SRCS) list(SORT minetest_SRCS)

@ -23,12 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // for g_settings #include "main.h" // for g_settings
#include "map.h" #include "map.h"
#include "clientmap.h" // MapDrawControl #include "clientmap.h" // MapDrawControl
#include "mesh.h"
#include "player.h" #include "player.h"
#include "tile.h"
#include <cmath> #include <cmath>
#include "settings.h" #include "settings.h"
#include "itemdef.h" // For wield visualization #include "wieldmesh.h"
#include "noise.h" // easeCurve #include "noise.h" // easeCurve
#include "gamedef.h" #include "gamedef.h"
#include "sound.h" #include "sound.h"
@ -50,7 +48,6 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
m_wieldmgr(NULL), m_wieldmgr(NULL),
m_wieldnode(NULL), m_wieldnode(NULL),
m_wieldlight(0),
m_draw_control(draw_control), m_draw_control(draw_control),
m_gamedef(gamedef), m_gamedef(gamedef),
@ -77,12 +74,9 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
m_digging_anim(0), m_digging_anim(0),
m_digging_button(-1), m_digging_button(-1),
m_dummymesh(createCubeMesh(v3f(1,1,1))),
m_wield_change_timer(0.125), m_wield_change_timer(0.125),
m_wield_mesh_next(NULL), m_wield_item_next(),
m_previous_playeritem(-1),
m_previous_itemname(""),
m_camera_mode(CAMERA_MODE_FIRST) 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. // all other 3D scene nodes and before the GUI.
m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr = smgr->createNewSceneManager();
m_wieldmgr->addCameraSceneNode(); 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() Camera::~Camera()
{ {
m_wieldmgr->drop(); m_wieldmgr->drop();
delete m_dummymesh;
} }
bool Camera::successfullyCreated(std::wstring& error_message) 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; bool was_under_zero = m_wield_change_timer < 0;
if(m_wield_change_timer < 0.125) m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125);
m_wield_change_timer += dtime;
if(m_wield_change_timer > 0.125)
m_wield_change_timer = 0.125;
if (m_wield_change_timer >= 0 && was_under_zero) if (m_wield_change_timer >= 0 && was_under_zero)
{ m_wieldnode->setItem(m_wield_item_next, m_gamedef);
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_view_bobbing_state != 0) 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_position = v3f(55, -35, 65);
//v3f wield_rotation = v3f(-100, 120, -100); //v3f wield_rotation = v3f(-100, 120, -100);
v3f wield_rotation = v3f(-100, 120, -100); v3f wield_rotation = v3f(-100, 120, -100);
if(m_wield_change_timer < 0) wield_position.Y += fabs(m_wield_change_timer)*320 - 40;
wield_position.Y -= 40 + m_wield_change_timer*320;
else
wield_position.Y -= 40 - m_wield_change_timer*320;
if(m_digging_anim < 0.05 || m_digging_anim > 0.5) if(m_digging_anim < 0.05 || m_digging_anim > 0.5)
{ {
f32 frac = 1.0; f32 frac = 1.0;
@ -486,7 +466,12 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
} }
m_wieldnode->setPosition(wield_position); m_wieldnode->setPosition(wield_position);
m_wieldnode->setRotation(wield_rotation); 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 // Render distance feedback loop
updateViewingRange(frametime, busytime); updateViewingRange(frametime, busytime);
@ -658,48 +643,20 @@ void Camera::setDigging(s32 button)
m_digging_button = 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; m_wield_change_timer = -m_wield_change_timer;
} else if (m_wield_change_timer == 0)
} else { m_wield_change_timer = -0.001;
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;
} }
} }
void Camera::drawWieldedTool(irr::core::matrix4* translation) void Camera::drawWieldedTool(irr::core::matrix4* translation)
{ {
// Set vertex colors of wield mesh according to light level // Clear Z buffer so that the wielded tool stay in front of world geometry
u8 li = m_wieldlight;
video::SColor color(255,li,li,li);
setMeshColor(m_wieldnode->getMesh(), color);
// Clear Z buffer
m_wieldmgr->getVideoDriver()->clearZBuffer(); m_wieldmgr->getVideoDriver()->clearZBuffer();
// Draw the wielded node (in a separate scene manager) // 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->setAspectRatio(m_cameranode->getAspectRatio());
cam->setFOV(72.0*M_PI/180.0); cam->setFOV(72.0*M_PI/180.0);
cam->setNearValue(0.1); cam->setNearValue(0.1);
cam->setFarValue(100); cam->setFarValue(1000);
if (translation != NULL) if (translation != NULL)
{ {
irr::core::matrix4 startMatrix = cam->getAbsoluteTransformation(); irr::core::matrix4 startMatrix = cam->getAbsoluteTransformation();

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class LocalPlayer; class LocalPlayer;
struct MapDrawControl; struct MapDrawControl;
class IGameDef; class IGameDef;
class WieldMeshSceneNode;
enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT}; enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT};
@ -127,7 +128,7 @@ public:
void setDigging(s32 button); void setDigging(s32 button);
// Replace the wielded item mesh // Replace the wielded item mesh
void wield(const ItemStack &item, u16 playeritem); void wield(const ItemStack &item);
// Draw the wielded tool. // Draw the wielded tool.
// This has to happen *after* the main scene is drawn. // This has to happen *after* the main scene is drawn.
@ -157,8 +158,8 @@ private:
scene::ICameraSceneNode* m_cameranode; scene::ICameraSceneNode* m_cameranode;
scene::ISceneManager* m_wieldmgr; scene::ISceneManager* m_wieldmgr;
scene::IMeshSceneNode* m_wieldnode; WieldMeshSceneNode* m_wieldnode;
u8 m_wieldlight; scene::ILightSceneNode* m_wieldlightnode;
// draw control // draw control
MapDrawControl& m_draw_control; MapDrawControl& m_draw_control;
@ -203,14 +204,9 @@ private:
// If 1, right-click digging animation // If 1, right-click digging animation
s32 m_digging_button; s32 m_digging_button;
//dummymesh for camera
irr::scene::IAnimatedMesh* m_dummymesh;
// Animation when changing wielded item // Animation when changing wielded item
f32 m_wield_change_timer; f32 m_wield_change_timer;
scene::IMesh *m_wield_mesh_next; ItemStack m_wield_item_next;
u16 m_previous_playeritem;
std::string m_previous_itemname;
CameraMode m_camera_mode; CameraMode m_camera_mode;
}; };

@ -41,6 +41,7 @@ class ITextureSource;
class IGameDef; class IGameDef;
class LocalPlayer; class LocalPlayer;
struct ItemStack; struct ItemStack;
class WieldMeshSceneNode;
class ClientActiveObject : public ActiveObject class ClientActiveObject : public ActiveObject
{ {
@ -58,8 +59,10 @@ public:
virtual bool getCollisionBox(aabb3f *toset){return false;} virtual bool getCollisionBox(aabb3f *toset){return false;}
virtual bool collideWithObjects(){return false;} virtual bool collideWithObjects(){return false;}
virtual v3f getPosition(){return v3f(0,0,0);} virtual v3f getPosition(){return v3f(0,0,0);}
virtual scene::ISceneNode *getSceneNode(){return NULL;}
virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;} virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;} virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
virtual WieldMeshSceneNode *getWieldMeshSceneNode(){return NULL;}
virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;} virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;}
virtual bool isPlayer() const {return false;} virtual bool isPlayer() const {return false;}
virtual bool isLocalPlayer() const {return false;} virtual bool isLocalPlayer() const {return false;}

@ -45,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include "main.h" // g_settings #include "main.h" // g_settings
#include "camera.h" // CameraModes #include "camera.h" // CameraModes
#include "wieldmesh.h"
#include "log.h" #include "log.h"
class Settings; 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_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
m_meshnode(NULL), m_meshnode(NULL),
m_animated_meshnode(NULL), m_animated_meshnode(NULL),
m_wield_meshnode(NULL),
m_spritenode(NULL), m_spritenode(NULL),
m_textnode(NULL), m_textnode(NULL),
m_position(v3f(0,10*BS,0)), m_position(v3f(0,10*BS,0)),
@ -683,38 +685,47 @@ core::aabbox3d<f32>* GenericCAO::getSelectionBox()
v3f GenericCAO::getPosition() v3f GenericCAO::getPosition()
{ {
if(getParent() != NULL) if (getParent() != NULL) {
{ scene::ISceneNode *node = getSceneNode();
if(m_meshnode) if (node)
return m_meshnode->getAbsolutePosition(); return node->getAbsolutePosition();
if(m_animated_meshnode) else
return m_animated_meshnode->getAbsolutePosition();
if(m_spritenode)
return m_spritenode->getAbsolutePosition();
return m_position; return m_position;
} }
return pos_translator.vect_show; return pos_translator.vect_show;
} }
scene::IMeshSceneNode* GenericCAO::getMeshSceneNode() scene::ISceneNode* GenericCAO::getSceneNode()
{ {
if (m_meshnode) if (m_meshnode)
return 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; return NULL;
} }
scene::IMeshSceneNode* GenericCAO::getMeshSceneNode()
{
return m_meshnode;
}
scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode() scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode()
{ {
if(m_animated_meshnode)
return m_animated_meshnode; return m_animated_meshnode;
return NULL; }
WieldMeshSceneNode* GenericCAO::getWieldMeshSceneNode()
{
return m_wield_meshnode;
} }
scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode() scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode()
{ {
if(m_spritenode)
return m_spritenode; return m_spritenode;
return NULL;
} }
void GenericCAO::setAttachments() void GenericCAO::setAttachments()
@ -769,6 +780,12 @@ void GenericCAO::removeFromScene(bool permanent)
m_animated_meshnode->drop(); m_animated_meshnode->drop();
m_animated_meshnode = NULL; m_animated_meshnode = NULL;
} }
if(m_wield_meshnode)
{
m_wield_meshnode->remove();
m_wield_meshnode->drop();
m_wield_meshnode = NULL;
}
if(m_spritenode) if(m_spritenode)
{ {
m_spritenode->remove(); m_spritenode->remove();
@ -789,7 +806,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
m_smgr = smgr; m_smgr = smgr;
m_irr = irr; m_irr = irr;
if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL) if (getSceneNode() != NULL)
return; return;
m_visuals_expired = false; m_visuals_expired = false;
@ -918,28 +935,23 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl; errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
} }
else if(m_prop.visual == "wielditem") { else if(m_prop.visual == "wielditem") {
infostream<<"GenericCAO::addToScene(): node"<<std::endl; infostream<<"GenericCAO::addToScene(): wielditem"<<std::endl;
infostream<<"textures: "<<m_prop.textures.size()<<std::endl; infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
if(m_prop.textures.size() >= 1){ if(m_prop.textures.size() >= 1){
infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl; infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
IItemDefManager *idef = m_gamedef->idef(); IItemDefManager *idef = m_gamedef->idef();
ItemStack item(m_prop.textures[0], 1, 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 m_wield_meshnode = new WieldMeshSceneNode(
scene::IMeshManipulator *manip = smgr->getRootSceneNode(), smgr, -1);
irr->getVideoDriver()->getMeshManipulator(); m_wield_meshnode->setItem(item, m_gamedef);
scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh); m_wield_meshnode->grab();
m_meshnode = smgr->addMeshSceneNode(mesh, NULL); m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2,
m_meshnode->grab();
mesh->drop();
m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
m_prop.visual_size.Y/2, m_prop.visual_size.Y/2,
m_prop.visual_size.X/2)); m_prop.visual_size.X/2));
u8 li = m_last_light; 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 { } else {
infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
@ -947,13 +959,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
} }
updateTextures(""); updateTextures("");
scene::ISceneNode *node = NULL; scene::ISceneNode *node = getSceneNode();
if(m_spritenode)
node = m_spritenode;
else if(m_animated_meshnode)
node = m_animated_meshnode;
else if(m_meshnode)
node = m_meshnode;
if (node && m_is_player && !m_is_local_player) { if (node && m_is_player && !m_is_local_player) {
// Add a text node for showing the name // Add a text node for showing the name
gui::IGUIEnvironment* gui = irr->getGUIEnvironment(); gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
@ -981,6 +987,8 @@ void GenericCAO::updateLight(u8 light_at_pos)
setMeshColor(m_meshnode->getMesh(), color); setMeshColor(m_meshnode->getMesh(), color);
if(m_animated_meshnode) if(m_animated_meshnode)
setMeshColor(m_animated_meshnode->getMesh(), color); setMeshColor(m_animated_meshnode->getMesh(), color);
if(m_wield_meshnode)
m_wield_meshnode->setColor(color);
if(m_spritenode) if(m_spritenode)
m_spritenode->setColor(color); m_spritenode->setColor(color);
} }
@ -996,24 +1004,16 @@ void GenericCAO::updateNodePos()
if (getParent() != NULL) if (getParent() != NULL)
return; return;
scene::ISceneNode *node = getSceneNode();
if (node) {
v3s16 camera_offset = m_env->getCameraOffset(); v3s16 camera_offset = m_env->getCameraOffset();
if(m_meshnode) node->setPosition(pos_translator.vect_show - intToFloat(camera_offset, BS));
{ if (node != m_spritenode) { // rotate if not a sprite
m_meshnode->setPosition(pos_translator.vect_show-intToFloat(camera_offset, BS)); v3f rot = node->getRotation();
v3f rot = m_meshnode->getRotation();
rot.Y = -m_yaw; rot.Y = -m_yaw;
m_meshnode->setRotation(rot); node->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));
} }
} }
@ -1107,20 +1107,10 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
continue; continue;
} }
ClientActiveObject *obj = m_env->getActiveObject(*ci); ClientActiveObject *obj = m_env->getActiveObject(*ci);
if(obj) if (obj) {
{ scene::ISceneNode *child_node = obj->getSceneNode();
scene::IMeshSceneNode *m_child_meshnode if (child_node)
= obj->getMeshSceneNode(); child_node->setParent(m_smgr->getRootSceneNode());
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());
} }
++ci; ++ci;
} }
@ -1140,14 +1130,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
} }
// Make sure m_is_visible is always applied // Make sure m_is_visible is always applied
if(m_meshnode) scene::ISceneNode *node = getSceneNode();
m_meshnode->setVisible(m_is_visible); if (node)
if(m_animated_meshnode) node->setVisible(m_is_visible);
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);
if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht 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(getParent() == NULL || m_attached_to_local) // Detach or don't attach
{ {
if(m_meshnode) scene::ISceneNode *node = getSceneNode();
{ if (node) {
v3f old_position = m_meshnode->getAbsolutePosition(); v3f old_position = node->getAbsolutePosition();
v3f old_rotation = m_meshnode->getRotation(); v3f old_rotation = node->getRotation();
m_meshnode->setParent(m_smgr->getRootSceneNode()); node->setParent(m_smgr->getRootSceneNode());
m_meshnode->setPosition(old_position); node->setPosition(old_position);
m_meshnode->setRotation(old_rotation); node->setRotation(old_rotation);
m_meshnode->updateAbsolutePosition(); node->updateAbsolutePosition();
} }
if(m_animated_meshnode) if (m_is_local_player) {
{
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)
{
LocalPlayer *player = m_env->getLocalPlayer(); LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = false; player->isAttached = false;
} }
} }
else // Attach else // Attach
{ {
scene::IMeshSceneNode *parent_mesh = NULL; scene::ISceneNode *my_node = getSceneNode();
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; scene::ISceneNode *parent_node = getParent()->getSceneNode();
if(parent_animated_mesh && m_attachment_bone != "") scene::IAnimatedMeshSceneNode *parent_animated_mesh_node =
{ getParent()->getAnimatedMeshSceneNode();
parent_bone = if (parent_animated_mesh_node && m_attachment_bone != "") {
parent_animated_mesh->getJointNode(m_attachment_bone.c_str()); parent_node = parent_animated_mesh_node->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 if (my_node && parent_node) {
// TODO: Perhaps use polymorphism here to save code duplication my_node->setParent(parent_node);
if(m_meshnode) my_node->setPosition(m_attachment_position);
{ my_node->setRotation(m_attachment_rotation);
if(parent_bone) my_node->updateAbsolutePosition();
{
m_meshnode->setParent(parent_bone);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
} }
else if (m_is_local_player) {
{
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();
}
}
}
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(m_is_local_player)
{
LocalPlayer *player = m_env->getLocalPlayer(); LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = true; player->isAttached = true;
} }

@ -70,6 +70,7 @@ private:
core::aabbox3d<f32> m_selection_box; core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_meshnode; scene::IMeshSceneNode *m_meshnode;
scene::IAnimatedMeshSceneNode *m_animated_meshnode; scene::IAnimatedMeshSceneNode *m_animated_meshnode;
WieldMeshSceneNode *m_wield_meshnode;
scene::IBillboardSceneNode *m_spritenode; scene::IBillboardSceneNode *m_spritenode;
scene::ITextSceneNode* m_textnode; scene::ITextSceneNode* m_textnode;
v3f m_position; v3f m_position;
@ -131,10 +132,14 @@ public:
v3f getPosition(); v3f getPosition();
scene::ISceneNode *getSceneNode();
scene::IMeshSceneNode *getMeshSceneNode(); scene::IMeshSceneNode *getMeshSceneNode();
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(); scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode();
WieldMeshSceneNode *getWieldMeshSceneNode();
scene::IBillboardSceneNode *getSpriteSceneNode(); scene::IBillboardSceneNode *getSpriteSceneNode();
inline bool isPlayer() const inline bool isPlayer() const

@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER #ifndef SERVER
#include "clientmap.h" #include "clientmap.h"
#include "localplayer.h" #include "localplayer.h"
#include "mapblock_mesh.h"
#include "event.h" #include "event.h"
#endif #endif
#include "daynightratio.h" #include "daynightratio.h"
@ -2330,19 +2331,26 @@ void ClientEnvironment::step(float dtime)
player->move(dtime, this, 100*BS); player->move(dtime, this, 100*BS);
} }
}
// Update lighting on all players on client // Update lighting on local player (used for wield item)
float light = 1.0; u32 day_night_ratio = getDayNightRatio();
try{ {
// Get node at head // Get node at head
v3s16 p = player->getLightPosition();
MapNode n = m_map->getNode(p); // On InvalidPositionException, use this as default
light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef()); // (day: LIGHT_SUN, night: 0)
} MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
catch(InvalidPositionException &e){
light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0); try {
} v3s16 p = lplayer->getLightPosition();
player->light = light; 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);
} }
/* /*
@ -2367,10 +2375,10 @@ void ClientEnvironment::step(float dtime)
// Get node at head // Get node at head
v3s16 p = obj->getLightPosition(); v3s16 p = obj->getLightPosition();
MapNode n = m_map->getNode(p); 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){ catch(InvalidPositionException &e){
light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); light = blend_light(day_night_ratio, LIGHT_SUN, 0);
} }
obj->updateLight(light); obj->updateLight(light);
} }

@ -3656,7 +3656,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
if (mlist && (client->getPlayerItem() < mlist->getSize())) { if (mlist && (client->getPlayerItem() < mlist->getSize())) {
ItemStack item = mlist->getItem(client->getPlayerItem()); ItemStack item = mlist->getItem(client->getPlayerItem());
camera->wield(item, client->getPlayerItem()); camera->wield(item);
} }
runData->update_wielded_item_trigger = false; runData->update_wielded_item_trigger = false;
} }

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER #ifndef SERVER
#include "mapblock_mesh.h" #include "mapblock_mesh.h"
#include "mesh.h" #include "mesh.h"
#include "wieldmesh.h"
#include "tile.h" #include "tile.h"
#endif #endif
#include "log.h" #include "log.h"
@ -330,63 +331,40 @@ public:
ITextureSource *tsrc = gamedef->getTextureSource(); ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = gamedef->getNodeDefManager(); INodeDefManager *nodedef = gamedef->getNodeDefManager();
IrrlichtDevice *device = tsrc->getDevice(); const ItemDefinition &def = get(name);
video::IVideoDriver *driver = device->getVideoDriver();
const ItemDefinition *def = &get(name);
// Create new ClientCached // Create new ClientCached
cc = new ClientCached(); cc = new ClientCached();
bool need_node_mesh = false;
// Create an inventory texture // Create an inventory texture
cc->inventory_texture = NULL; cc->inventory_texture = NULL;
if(def->inventory_image != "") if(def.inventory_image != "")
{ cc->inventory_texture = tsrc->getTexture(def.inventory_image);
cc->inventory_texture = tsrc->getTexture(def->inventory_image);
}
else if(def->type == ITEM_NODE)
{
need_node_mesh = true;
}
// Create a wield mesh // Additional processing for nodes:
assert(cc->wield_mesh == NULL); // - Create a wield mesh if WieldMeshSceneNode can't render
if(def->type == ITEM_NODE && def->wield_image == "") // the node on its own.
{ // - If inventory_texture isn't set yet, create one using
need_node_mesh = true; // render-to-texture.
} if (def.type == ITEM_NODE) {
else if(def->wield_image != "" || def->inventory_image != "") // Get node properties
{ content_t id = nodedef->getId(name);
// 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 "
<<def->name<<std::endl;
}
}
if(need_node_mesh)
{
/*
Get node properties
*/
content_t id = nodedef->getId(def->name);
const ContentFeatures &f = nodedef->get(id); const ContentFeatures &f = nodedef->get(id);
bool need_rtt_mesh = cc->inventory_texture == NULL;
// 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);
scene::IMesh *node_mesh = NULL;
bool reenable_shaders = false;
if (need_rtt_mesh || need_wield_mesh) {
u8 param1 = 0; u8 param1 = 0;
if (f.param_type == CPT_LIGHT) if (f.param_type == CPT_LIGHT)
param1 = 0xee; param1 = 0xee;
@ -394,7 +372,6 @@ public:
/* /*
Make a mesh from the node Make a mesh from the node
*/ */
bool reenable_shaders = false;
if (g_settings->getBool("enable_shaders")) { if (g_settings->getBool("enable_shaders")) {
reenable_shaders = true; reenable_shaders = true;
g_settings->setBool("enable_shaders", false); g_settings->setBool("enable_shaders", false);
@ -406,28 +383,26 @@ public:
MapNode mesh_make_node(id, param1, param2); MapNode mesh_make_node(id, param1, param2);
mesh_make_data.fillSingleNode(&mesh_make_node); mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
scene::IMesh *node_mesh = mapblock_mesh.getMesh(); node_mesh = mapblock_mesh.getMesh();
assert(node_mesh); node_mesh->grab();
video::SColor c(255, 255, 255, 255); video::SColor c(255, 255, 255, 255);
setMeshColor(node_mesh, c); setMeshColor(node_mesh, c);
/* // scale and translate the mesh so it's a
Scale and translate the mesh so it's a unit cube // unit cube centered on the origin
centered on the origin
*/
scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
}
/* /*
Draw node mesh into a render target texture Draw node mesh into a render target texture
*/ */
if(cc->inventory_texture == NULL) if (need_rtt_mesh) {
{
TextureFromMeshParams params; TextureFromMeshParams params;
params.mesh = node_mesh; params.mesh = node_mesh;
params.dim.set(64, 64); params.dim.set(64, 64);
params.rtt_texture_name = "INVENTORY_" params.rtt_texture_name = "INVENTORY_"
+ def->name + "_RTT"; + def.name + "_RTT";
params.delete_texture_on_shutdown = true; params.delete_texture_on_shutdown = true;
params.camera_position.set(0, 1.0, -1.5); params.camera_position.set(0, 1.0, -1.5);
params.camera_position.rotateXZBy(45); params.camera_position.rotateXZBy(45);
@ -449,8 +424,7 @@ public:
tsrc->generateTextureFromMesh(params); tsrc->generateTextureFromMesh(params);
// render-to-target didn't work // render-to-target didn't work
if(cc->inventory_texture == NULL) if (cc->inventory_texture == NULL) {
{
cc->inventory_texture = cc->inventory_texture =
tsrc->getTexture(f.tiledef[0].name); tsrc->getTexture(f.tiledef[0].name);
} }
@ -459,16 +433,16 @@ public:
/* /*
Use the node mesh as the wield mesh Use the node mesh as the wield mesh
*/ */
if (need_wield_mesh) {
// 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 = node_mesh;
cc->wield_mesh->grab(); cc->wield_mesh->grab();
// no way reference count can be smaller than 2 in this place! // no way reference count can be smaller than 2 in this place!
assert(cc->wield_mesh->getReferenceCount() >= 2); assert(cc->wield_mesh->getReferenceCount() >= 2);
}
if (node_mesh)
node_mesh->drop();
if (reenable_shaders) if (reenable_shaders)
g_settings->setBool("enable_shaders",true); g_settings->setBool("enable_shaders",true);

@ -48,6 +48,7 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
last_animation(NO_ANIM), last_animation(NO_ANIM),
hotbar_image(""), hotbar_image(""),
hotbar_selected_image(""), hotbar_selected_image(""),
light_color(255,255,255,255),
m_sneak_node(32767,32767,32767), m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false), m_sneak_node_exists(false),
m_old_node_below(32767,32767,32767), m_old_node_below(32767,32767,32767),

@ -71,6 +71,8 @@ public:
std::string hotbar_image; std::string hotbar_image;
std::string hotbar_selected_image; std::string hotbar_selected_image;
video::SColor light_color;
GenericCAO* getCAO() const { GenericCAO* getCAO() const {
return m_cao; return m_cao;
} }

@ -320,7 +320,7 @@ u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
Converts from day + night color values (0..255) Converts from day + night color values (0..255)
and a given daynight_ratio to the final SColor shown on screen. 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) u8 day, u8 night, u32 daynight_ratio)
{ {
s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000; s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;

@ -195,6 +195,11 @@ u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);
u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef); u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef);
u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data); 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 // Retrieves the TileSpec of a face of a node
// Adds MATERIAL_FLAG_CRACK if the node is cracked // Adds MATERIAL_FLAG_CRACK if the node is cracked
TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data); TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data);

@ -91,218 +91,6 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale)
return anim_mesh; 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<u32> 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) void scaleMesh(scene::IMesh *mesh, v3f scale)
{ {
if(mesh == NULL) if(mesh == NULL)
@ -523,6 +311,8 @@ scene::IMesh* convertNodeboxNodeToMesh(ContentFeatures *f)
for (u16 j = 0; j < 6; j++) for (u16 j = 0; j < 6; j++)
{ {
scene::IMeshBuffer *buf = new scene::SMeshBuffer(); 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); dst_mesh->addMeshBuffer(buf);
buf->drop(); buf->drop();
} }

@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "nodedef.h" #include "nodedef.h"
#include <string>
/* /*
Create a new cube mesh. 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); 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 Multiplies each vertex coordinate by the specified scaling factors
(componentwise vector multiplication). (componentwise vector multiplication).

@ -39,7 +39,6 @@ Player::Player(IGameDef *gamedef, const char *name):
is_climbing(false), is_climbing(false),
swimming_vertical(false), swimming_vertical(false),
camera_barely_in_ceiling(false), camera_barely_in_ceiling(false),
light(0),
inventory(gamedef->idef()), inventory(gamedef->idef()),
hp(PLAYER_MAX_HP), hp(PLAYER_MAX_HP),
hurt_tilt_timer(0), hurt_tilt_timer(0),

@ -249,8 +249,6 @@ public:
bool swimming_vertical; bool swimming_vertical;
bool camera_barely_in_ceiling; bool camera_barely_in_ceiling;
u8 light;
Inventory inventory; Inventory inventory;
f32 movement_acceleration_default; f32 movement_acceleration_default;

@ -199,6 +199,16 @@ struct TestUtilities: public TestBase
UASSERT(is_number("123") == true); UASSERT(is_number("123") == true);
UASSERT(is_number("") == false); UASSERT(is_number("") == false);
UASSERT(is_number("123a") == 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);
} }
}; };

@ -361,5 +361,10 @@ inline float cycle_shift(float value, float by = 0, float max = 1)
return value + by; return value + by;
} }
inline bool is_power_of_two(u32 n)
{
return n != 0 && (n & (n-1)) == 0;
}
#endif #endif

380
src/wieldmesh.cpp Normal file

@ -0,0 +1,380 @@
/*
Minetest
Copyright (C) 2010-2014 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 "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 <map>
#include <IMeshManipulator.h>
#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<int, scene::IMesh*>::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<u32> 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<int, scene::IMesh*>::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<int, scene::IMesh*> 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);
}

71
src/wieldmesh.h Normal file

@ -0,0 +1,71 @@
/*
Minetest
Copyright (C) 2010-2014 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 WIELDMESH_HEADER
#define WIELDMESH_HEADER
#include "irrlichttypes_extrabloated.h"
#include <string>
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<f32>& 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<f32> m_bounding_box;
};
#endif