Added sprite extruder

master
Kahrl 2011-09-19 03:01:11 +02:00
parent 4ed837bcfa
commit 36bcbca9ac
8 changed files with 425 additions and 103 deletions

View File

@ -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<f32>& 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<u32> 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;
}

View File

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

View File

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

View File

@ -210,7 +210,6 @@ public:
LocalPlayer* getLocalPlayer();
void setPlayerWield(scene::ISceneNode *wield);
void setPlayerControl(PlayerControl &control);
void selectPlayerItem(u16 item);

View File

@ -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<<DTIME<<"Left-clicked object"<<std::endl;
client.clickObject(0, selected_object->getBlock()->getPos(),
selected_object->getId(), g_selected_item);
camera.setDigging(true);
}
else if(input->getRightClicked())
{
@ -1619,6 +1582,7 @@ void the_game(
std::cout<<DTIME<<"Left-clicked object"<<std::endl;
client.clickActiveObject(0,
selected_active_object->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<<DTIME<<"Left button released (stopped digging)"
<<std::endl;
client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
camera.setDigging(false);
}
if(input->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"<<std::endl;
client.getLocalInventory(local_inventory);
// Update wielded tool
InventoryList *mlist = local_inventory.getList("main");
InventoryItem *item = NULL;
if(mlist != NULL)
item = mlist->getItem(g_selected_item);
camera.wield(item);
}
/*

View File

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

View File

@ -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<CollisionInfo> *collision_info)
{

View File

@ -357,8 +357,6 @@ public:
return true;
}
void wieldItem(u16 item);
void move(f32 dtime, Map &map, f32 pos_max_d,
core::list<CollisionInfo> *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;