From 36bcbca9acabbc47976d3d7625ffb1c9396b8fdc Mon Sep 17 00:00:00 2001 From: Kahrl Date: Mon, 19 Sep 2011 03:01:11 +0200 Subject: [PATCH] Added sprite extruder --- src/camera.cpp | 349 ++++++++++++++++++++++++++++++++++++++++++++++++ src/camera.h | 66 ++++++++- src/client.cpp | 8 -- src/client.h | 1 - src/game.cpp | 70 ++-------- src/inventory.h | 11 -- src/player.cpp | 19 --- src/player.h | 4 - 8 files changed, 425 insertions(+), 103 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 43009bd7c..aae36c512 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -63,6 +63,8 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_headnode = smgr->addEmptySceneNode(m_playernode); m_cameranode = smgr->addCameraSceneNode(smgr->getRootSceneNode()); m_cameranode->bindTargetAndRotation(true); + m_wieldnode = new ExtrudedSpriteSceneNode(smgr->getRootSceneNode(), smgr, -1, v3f(0, 120, 10), v3f(0, 0, 0), v3f(100, 100, 100)); + //m_wieldnode = new ExtrudedSpriteSceneNode(smgr->getRootSceneNode(), smgr, -1); updateSettings(); } @@ -341,3 +343,350 @@ void Camera::updateSettings() m_wanted_frametime = 1.0 / wanted_fps; } +void Camera::wield(InventoryItem* item) +{ + if (item != NULL) + { + dstream << "wield item: " << item->getName() << std::endl; + m_wieldnode->setSprite(item->getImageRaw()); + m_wieldnode->setVisible(true); + } + else + { + dstream << "wield item: none" << std::endl; + m_wieldnode->setVisible(false); + } +} + +void Camera::setDigging(bool digging) +{ + // TODO +} + + +ExtrudedSpriteSceneNode::ExtrudedSpriteSceneNode( + scene::ISceneNode* parent, + scene::ISceneManager* mgr, + s32 id, + const v3f& position, + const v3f& rotation, + const v3f& scale +): + ISceneNode(parent, mgr, id, position, rotation, scale) +{ + m_meshnode = mgr->addMeshSceneNode(NULL, this, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + m_thickness = 0.1; + m_cubemesh = NULL; + m_is_cube = false; +} + +ExtrudedSpriteSceneNode::~ExtrudedSpriteSceneNode() +{ + removeChild(m_meshnode); + if (m_cubemesh) + m_cubemesh->drop(); +} + +void ExtrudedSpriteSceneNode::setSprite(video::ITexture* texture) +{ + if (texture == NULL) + { + m_meshnode->setVisible(false); + return; + } + + io::path name = getExtrudedName(texture); + scene::IMeshCache* cache = SceneManager->getMeshCache(); + scene::IAnimatedMesh* mesh = cache->getMeshByName(name); + if (mesh != NULL) + { + // Extruded texture has been found in cache. + m_meshnode->setMesh(mesh); + } + else + { + // Texture was not yet extruded, do it now and save in cache + mesh = extrude(texture); + if (mesh == NULL) + { + dstream << "Warning: failed to extrude sprite" << std::endl; + m_meshnode->setVisible(false); + return; + } + cache->addMesh(name, mesh); + m_meshnode->setMesh(mesh); + mesh->drop(); + } + + m_meshnode->setScale(v3f(1, 1, m_thickness)); + m_meshnode->getMaterial(0).setTexture(0, texture); + m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false); + m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false); + m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + m_meshnode->setVisible(true); + m_is_cube = false; +} + +void ExtrudedSpriteSceneNode::setCube(video::ITexture* texture) +{ + if (texture == NULL) + { + m_meshnode->setVisible(false); + return; + } + + if (m_cubemesh == NULL) + m_cubemesh = SceneManager->getGeometryCreator()->createCubeMesh(v3f(1)); + + m_meshnode->setMesh(m_cubemesh); + m_meshnode->setScale(v3f(1)); + m_meshnode->getMaterial(0).setTexture(0, texture); + m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false); + m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false); + m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + m_meshnode->setVisible(true); + m_is_cube = true; +} + +void ExtrudedSpriteSceneNode::removeSpriteFromCache(video::ITexture* texture) +{ + scene::IMeshCache* cache = SceneManager->getMeshCache(); + scene::IAnimatedMesh* mesh = cache->getMeshByName(getExtrudedName(texture)); + if (mesh != NULL) + cache->removeMesh(mesh); +} + +void ExtrudedSpriteSceneNode::setSpriteThickness(f32 thickness) +{ + m_thickness = thickness; + if (!m_is_cube) + m_meshnode->setScale(v3f(1, 1, thickness)); +} + +const core::aabbox3d& ExtrudedSpriteSceneNode::getBoundingBox() const +{ + return m_meshnode->getBoundingBox(); +} + +void ExtrudedSpriteSceneNode::OnRegisterSceneNode() +{ + if (IsVisible) + SceneManager->registerNodeForRendering(this); + ISceneNode::OnRegisterSceneNode(); +} + +void ExtrudedSpriteSceneNode::render() +{ + // do nothing +} + +io::path ExtrudedSpriteSceneNode::getExtrudedName(video::ITexture* texture) +{ + io::path path = texture->getName(); + path.append("/[extruded]"); + return path; +} + +scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrudeARGB(u32 width, u32 height, u8* data) +{ + const s32 argb_wstep = 4 * width; + 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[(width+2) * (height+2)]; + u32 wstep = width + 2; + for (u32 y = 0; y < height + 2; ++y) + { + u8* scanline = solidity + y * wstep; + if (y == 0 || y == height + 1) + { + for (u32 x = 0; x < width + 2; ++x) + scanline[x] = 0; + } + else + { + scanline[0] = 0; + u8* argb_scanline = data + (y - 1) * argb_wstep; + for (u32 x = 0; x < width; ++x) + scanline[x+1] = (argb_scanline[x*4+3] >= alpha_threshold); + scanline[width + 1] = 0; + } + } + + // without this, there would be occasional "holes" in the mesh + f32 eps = 0.01; + + for (u32 y = 0; y <= height; ++y) + { + u8* scanline = solidity + y * wstep + 1; + for (u32 x = 0; x <= width; ++x) + { + if (scanline[x] && !scanline[x + wstep]) + { + u32 xx = x + 1; + while (scanline[xx] && !scanline[xx + wstep]) + ++xx; + f32 vx1 = (x - eps) / (f32) width - 0.5; + f32 vx2 = (xx + eps) / (f32) width - 0.5; + f32 vy = 0.5 - (y - eps) / (f32) height; + f32 tx1 = x / (f32) width; + f32 tx2 = xx / (f32) width; + f32 ty = (y - 0.5) / (f32) height; + 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) width - 0.5; + f32 vx2 = (xx + eps) / (f32) width - 0.5; + f32 vy = 0.5 - (y + eps) / (f32) height; + f32 tx1 = x / (f32) width; + f32 tx2 = xx / (f32) width; + f32 ty = (y + 0.5) / (f32) height; + 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 <= width; ++x) + { + u8* scancol = solidity + x + wstep; + for (u32 y = 0; y <= height; ++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) width - 0.5; + f32 vy1 = 0.5 - (y - eps) / (f32) height; + f32 vy2 = 0.5 - (yy + eps) / (f32) height; + f32 tx = (x - 0.5) / (f32) width; + f32 ty1 = y / (f32) height; + f32 ty2 = yy / (f32) height; + 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) width - 0.5; + f32 vy1 = 0.5 - (y - eps) / (f32) height; + f32 vy2 = 0.5 - (yy + eps) / (f32) height; + f32 tx = (x + 0.5) / (f32) width; + f32 ty1 = y / (f32) height; + f32 ty2 = yy / (f32) height; + 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; + } + } + } + + // Add to mesh + scene::SMesh* mesh = new scene::SMesh(); + mesh->addMeshBuffer(buf); + buf->drop(); + mesh->recalculateBoundingBox(); + scene::SAnimatedMesh* anim_mesh = new scene::SAnimatedMesh(mesh); + mesh->drop(); + return anim_mesh; +} + +scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrude(video::ITexture* texture) +{ + scene::IAnimatedMesh* mesh = NULL; + core::dimension2d size = texture->getSize(); + 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(true); + if (data == NULL) + return NULL; + mesh = extrudeARGB(size.Width, size.Height, (u8*) data); + texture->unlock(); + } + else + { + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + + video::IImage* img1 = driver->createImageFromData(format, size, texture->lock(true)); + 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->copyTo(img2); + img1->drop(); + + mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock()); + img2->unlock(); + img2->drop(); + } + img1->drop(); + } + return mesh; +} + diff --git a/src/camera.h b/src/camera.h index dd9f30551..08c03dd19 100644 --- a/src/camera.h +++ b/src/camera.h @@ -21,14 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CAMERA_HEADER #include "common_irrlicht.h" +#include "inventory.h" #include "utility.h" class LocalPlayer; class MapDrawControl; +class ExtrudedSpriteSceneNode; /* Client camera class, manages the player and camera scene nodes, the viewing distance - and performs view bobbing etc. + and performs view bobbing etc. It also displays the wielded tool in front of the + first-person camera. */ class Camera { @@ -59,6 +62,12 @@ public: return m_cameranode; } + // Get wielded item scene node. + inline ExtrudedSpriteSceneNode* getWieldNode() const + { + return m_wieldnode; + } + // Get the camera position (in absolute scene coordinates). // This has view bobbing applied. inline v3f getPosition() const @@ -107,12 +116,19 @@ public: // Update settings from g_settings void updateSettings(); + // Replace the wielded item mesh + void wield(InventoryItem* item); + + // Start or stop digging animation + void setDigging(bool digging); + private: // Scene manager and nodes scene::ISceneManager* m_smgr; scene::ISceneNode* m_playernode; scene::ISceneNode* m_headnode; scene::ICameraSceneNode* m_cameranode; + ExtrudedSpriteSceneNode* m_wieldnode; // draw control MapDrawControl& m_draw_control; @@ -151,5 +167,51 @@ private: f32 m_view_bobbing_speed; }; -#endif +/* + A scene node that displays a 2D mesh extruded into the third dimension, + to add an illusion of depth. + + Since this class was created to display the wielded tool of the local + player, and only tools and items are rendered like this (but not solid + content like stone and mud, which are shown as cubes), the option to + draw a textured cube instead is provided. + */ +class ExtrudedSpriteSceneNode: public scene::ISceneNode +{ +public: + ExtrudedSpriteSceneNode( + scene::ISceneNode* parent, + scene::ISceneManager* mgr, + s32 id = -1, + const v3f& position = v3f(0,0,0), + const v3f& rotation = v3f(0,0,0), + const v3f& scale = v3f(0,0,0)); + ~ExtrudedSpriteSceneNode(); + + void setSprite(video::ITexture* texture); + void setCube(const TileSpec faces[6]); + + f32 getSpriteThickness() const { return m_thickness; } + void setSpriteThickness(f32 thickness); + + void removeSpriteFromCache(video::ITexture* texture); + + virtual const core::aabbox3d& getBoundingBox() const; + virtual void OnRegisterSceneNode(); + virtual void render(); + +private: + scene::IMeshSceneNode* m_meshnode; + f32 m_thickness; + scene::IMesh* m_cubemesh; + bool m_is_cube; + + // internal extrusion helper methods + io::path getExtrudedName(video::ITexture* texture); + scene::IAnimatedMesh* extrudeARGB(u32 width, u32 height, u8* data); + scene::IAnimatedMesh* extrude(video::ITexture* texture); + scene::IMesh* createCubeMesh(); +}; + +#endif diff --git a/src/client.cpp b/src/client.cpp index 71a826a1f..81dedd144 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -2004,14 +2004,6 @@ LocalPlayer* Client::getLocalPlayer() return m_env.getLocalPlayer(); } -void Client::setPlayerWield(scene::ISceneNode *wield) -{ - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - player->wield = wield; -} - void Client::setPlayerControl(PlayerControl &control) { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out diff --git a/src/client.h b/src/client.h index e7a81a111..930987c02 100644 --- a/src/client.h +++ b/src/client.h @@ -210,7 +210,6 @@ public: LocalPlayer* getLocalPlayer(); - void setPlayerWield(scene::ISceneNode *wield); void setPlayerControl(PlayerControl &control); void selectPlayerItem(u16 item); diff --git a/src/game.cpp b/src/game.cpp index a712323b5..5e8db9b1d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -819,44 +819,6 @@ void the_game( f32 camera_yaw = 0; // "right/left" f32 camera_pitch = 0; // "up/down" - /* - Tool - */ - - v3f tool_wield_position(0.06*BS, -0.06*BS, 0.1*BS); - v3f tool_wield_rotation(-25, 180, -25); - float tool_wield_animation = 0.0; - scene::IMeshSceneNode *tool_wield; - { - scene::SMesh *mesh = new scene::SMesh(); - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - video::S3DVertex vertices[4] = - { - video::S3DVertex(-0.5,0,0, 0,0,0, c, 0,1), - video::S3DVertex(0.5,0,0, 0,0,0, c, 1,1), - video::S3DVertex(0.5,0.5,0, 0,0,0, c, 1,0), - video::S3DVertex(-0.5,0.5,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - - tool_wield = smgr->addMeshSceneNode(mesh, camera.getHeadNode()); - mesh->drop(); - } - tool_wield->setVisible(false); - tool_wield->setPosition(tool_wield_position); - tool_wield->setRotation(tool_wield_rotation); - - client.setPlayerWield(tool_wield); - /* Clouds */ @@ -1552,6 +1514,7 @@ void the_game( std::cout<getBlock()->getPos(), selected_object->getId(), g_selected_item); + camera.setDigging(true); } else if(input->getRightClicked()) { @@ -1619,6 +1582,7 @@ void the_game( std::cout<getId(), g_selected_item); + camera.setDigging(true); } else if(input->getRightClicked()) { @@ -1792,6 +1756,8 @@ void the_game( } dig_time += dtime; + + camera.setDigging(true); } } @@ -1859,16 +1825,6 @@ void the_game( nodepos_old = nodepos; } - else{ - } - - - if(input->getLeftState()) - // Tool animation loops 0.0 - 1.0 - tool_wield_animation = fmod(tool_wield_animation + dtime * 3.0, 1.0); - else - // Return tool to holding position if not digging - tool_wield_animation /= 1.5; } // selected_object == NULL @@ -1880,6 +1836,7 @@ void the_game( std::cout<getRightReleased()) { @@ -1985,16 +1942,6 @@ void the_game( ); } - /* - Animate tool - */ - { - f32 tool_wield_sin = sin(tool_wield_animation * PI); - tool_wield->setRotation(tool_wield_rotation - tool_wield_sin * 40.0); - tool_wield->setPosition(tool_wield_position - tool_wield_sin * BS / 30.0); - } - - /* Update gui stuff (0ms) */ @@ -2140,6 +2087,13 @@ void the_game( old_selected_item = g_selected_item; //std::cout<<"Updating local inventory"<getItem(g_selected_item); + camera.wield(item); } /* diff --git a/src/inventory.h b/src/inventory.h index ef8c499b1..4aa68d99e 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -56,8 +56,6 @@ public: // Return the name of the image for this item virtual std::string getBasename() { return ""; } // Shall return an image of the item (or NULL) - virtual video::ITexture * getImageRaw() { return NULL; } - // Shall return an image to show in the GUI (or NULL) virtual video::ITexture * getImage() { return NULL; } #endif // Shall return a text to show in the GUI @@ -156,7 +154,6 @@ public: video::ITexture * getImage() { return content_features(m_content).inventory_texture; - return NULL; } #endif std::string getText() @@ -388,14 +385,6 @@ public: return "cloud.png"; } - video::ITexture * getImageRaw() - { - if(g_texturesource == NULL) - return NULL; - - return g_texturesource->getTextureRaw(getBasename()); - } - video::ITexture * getImage() { if(g_texturesource == NULL) diff --git a/src/player.cpp b/src/player.cpp index 03ae24f45..7cfdfebb6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -309,31 +309,12 @@ LocalPlayer::LocalPlayer(): // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points hp = 0; - - // No tool wielded initially - wield = NULL; } LocalPlayer::~LocalPlayer() { } -void LocalPlayer::wieldItem(u16 item) -{ - m_selected_item = item; - - if(wield) { - InventoryItem* i = inventory.getList("main")->getItem(m_selected_item); - - if(i && strcmp(i->getName(), "ToolItem") == 0) { - wield->getMaterial(0).setTexture(0, i->getImageRaw()); - wield->setVisible(true); - } - else - wield->setVisible(false); - } -} - void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, core::list *collision_info) { diff --git a/src/player.h b/src/player.h index 0f2c40011..ad0fb5ff9 100644 --- a/src/player.h +++ b/src/player.h @@ -357,8 +357,6 @@ public: return true; } - void wieldItem(u16 item); - void move(f32 dtime, Map &map, f32 pos_max_d, core::list *collision_info); void move(f32 dtime, Map &map, f32 pos_max_d); @@ -367,8 +365,6 @@ public: PlayerControl control; - scene::ISceneNode *wield; - private: // This is used for determining the sneaking range v3s16 m_sneak_node;