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

@ -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 += \

@ -466,6 +466,7 @@ set(minetest_SRCS
shader.cpp
sky.cpp
tile.cpp
wieldmesh.cpp
${minetest_SCRIPT_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 "map.h"
#include "clientmap.h" // MapDrawControl
#include "mesh.h"
#include "player.h"
#include "tile.h"
#include <cmath>
#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();

@ -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 @@ public:
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 @@ private:
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 @@ private:
// 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;
};

@ -41,6 +41,7 @@ class ITextureSource;
class IGameDef;
class LocalPlayer;
struct ItemStack;
class WieldMeshSceneNode;
class ClientActiveObject : public ActiveObject
{
@ -58,8 +59,10 @@ public:
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;}

@ -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<f32>* 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::ISceneNode* GenericCAO::getSceneNode()
{
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()
{
if(m_meshnode)
return m_meshnode;
return NULL;
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 "<<m_prop.mesh<<std::endl;
}
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;
if(m_prop.textures.size() >= 1){
infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
IItemDefManager *idef = m_gamedef->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(): \""<<m_prop.visual
@ -947,14 +959,8 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
}
updateTextures("");
scene::ISceneNode *node = NULL;
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){
scene::ISceneNode *node = getSceneNode();
if (node && m_is_player && !m_is_local_player) {
// Add a text node for showing the name
gui::IGUIEnvironment* gui = irr->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<u16>::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::ISceneNode *my_node = getSceneNode();
scene::IBoneSceneNode *parent_bone = NULL;
if(parent_animated_mesh && m_attachment_bone != "")
{
parent_bone =
parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
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());
}
// 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 (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_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)
{
if (m_is_local_player) {
LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = true;
}

@ -70,6 +70,7 @@ private:
core::aabbox3d<f32> 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 @@ public:
v3f getPosition();
scene::ISceneNode *getSceneNode();
scene::IMeshSceneNode *getMeshSceneNode();
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode();
WieldMeshSceneNode *getWieldMeshSceneNode();
scene::IBillboardSceneNode *getSpriteSceneNode();
inline bool isPlayer() const

@ -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);
}

@ -3656,7 +3656,7 @@ void Game::updateFrame(std::vector<aabb3f> &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;
}

@ -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 @@ public:
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);
// 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 "
<<def->name<<std::endl;
}
}
if(need_node_mesh)
{
/*
Get node properties
*/
content_t id = nodedef->getId(def->name);
// 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);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
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;
/*
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 @@ public:
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 @@ public:
/*
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);
// no way reference count can be smaller than 2 in this place!
assert(cc->wield_mesh->getReferenceCount() >= 2);
}
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);
if (node_mesh)
node_mesh->drop();
if (reenable_shaders)
g_settings->setBool("enable_shaders",true);

@ -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),

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

@ -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;

@ -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);

@ -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<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)
{
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();
}

@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "nodedef.h"
#include <string>
/*
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).

@ -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),

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

@ -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);
}
};

@ -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

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