On-demand item meshes and textures

This commit is contained in:
Perttu Ahola 2012-11-30 18:12:32 +02:00
parent ba73e75ef9
commit bee170570d
8 changed files with 309 additions and 201 deletions

@ -529,7 +529,7 @@ void Camera::setDigging(s32 button)
void Camera::wield(const ItemStack &item)
{
IItemDefManager *idef = m_gamedef->idef();
scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh;
scene::IMesh *wield_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef);
if(wield_mesh)
{
m_wieldnode->setMesh(wield_mesh);

@ -1468,8 +1468,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
int num_files = readU16(is);
verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
<<num_files<<" files)"<<std::endl;
infostream<<"Client: Received media announcement: packet size: "
<<datasize<<std::endl;
core::list<MediaRequest> file_requests;
@ -2464,10 +2464,6 @@ void Client::afterContentReceived()
infostream<<"- Updating node textures"<<std::endl;
m_nodedef->updateTextures(m_tsrc);
// Update item textures and meshes
infostream<<"- Updating item textures and meshes"<<std::endl;
m_itemdef->updateTexturesAndMeshes(this);
// Start mesh update thread after setting up content definitions
infostream<<"- Starting mesh update thread"<<std::endl;
m_mesh_update_thread.Start();

@ -472,7 +472,7 @@ void ItemCAO::updateTexture()
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(is, idef);
texture = item.getDefinition(idef).inventory_texture;
texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
}
catch(SerializationError &e)
{
@ -957,7 +957,7 @@ public:
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 = item.getDefinition(idef).wield_mesh;
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 =

@ -1416,11 +1416,12 @@ void the_game(
g_gamecallback->changepassword_requested = false;
}
/*
Process TextureSource's queue
*/
/* Process TextureSource's queue */
tsrc->processQueue();
/* Process ItemDefManager's queue */
itemdef->processQueue(gamedef);
/*
Random calculations
*/

@ -46,7 +46,7 @@ void drawItemStack(video::IVideoDriver *driver,
return;
const ItemDefinition &def = item.getDefinition(gamedef->idef());
video::ITexture *texture = def.inventory_texture;
video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
// Draw the inventory texture
if(texture != NULL)
@ -519,7 +519,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(fimage, idef);
video::ITexture *texture = item.getDefinition(idef).inventory_texture;
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
std::string tooltip = item.getDefinition(idef).description;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),

@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif
#include "log.h"
#include "util/serialize.h"
#include "util/container.h"
#include "util/thread.h"
#include <map>
#include <set>
@ -71,14 +73,6 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
}
groups = def.groups;
node_placement_prediction = def.node_placement_prediction;
#ifndef SERVER
inventory_texture = def.inventory_texture;
if(def.wield_mesh)
{
wield_mesh = def.wield_mesh;
wield_mesh->grab();
}
#endif
return *this;
}
@ -91,10 +85,6 @@ void ItemDefinition::resetInitial()
{
// Initialize pointers to NULL so reset() does not delete undefined pointers
tool_capabilities = NULL;
#ifndef SERVER
inventory_texture = NULL;
wield_mesh = NULL;
#endif
reset();
}
@ -117,15 +107,6 @@ void ItemDefinition::reset()
groups.clear();
node_placement_prediction = "";
#ifndef SERVER
inventory_texture = NULL;
if(wield_mesh)
{
wield_mesh->drop();
wield_mesh = NULL;
}
#endif
}
void ItemDefinition::serialize(std::ostream &os) const
@ -203,13 +184,39 @@ void ItemDefinition::deSerialize(std::istream &is)
class CItemDefManager: public IWritableItemDefManager
{
#ifndef SERVER
struct ClientCached
{
video::ITexture *inventory_texture;
scene::IMesh *wield_mesh;
ClientCached():
inventory_texture(NULL),
wield_mesh(NULL)
{}
};
#endif
public:
CItemDefManager()
{
#ifndef SERVER
m_main_thread = get_current_thread_id();
#endif
clear();
}
virtual ~CItemDefManager()
{
#ifndef SERVER
const core::list<ClientCached*> &values = m_clientcached.getValues();
for(core::list<ClientCached*>::ConstIterator
i = values.begin(); i != values.end(); ++i)
{
ClientCached *cc = *i;
cc->wield_mesh->drop();
}
#endif
}
virtual const ItemDefinition& get(const std::string &name_) const
{
@ -256,6 +263,220 @@ public:
std::map<std::string, ItemDefinition*>::const_iterator i;
return m_item_definitions.find(name) != m_item_definitions.end();
}
#ifndef SERVER
ClientCached* createClientCachedDirect(const std::string &name,
IGameDef *gamedef) const
{
infostream<<"Lazily creating item texture and mesh for \""
<<name<<"\""<<std::endl;
// This is not thread-safe
assert(get_current_thread_id() == m_main_thread);
// Skip if already in cache
ClientCached *cc = NULL;
m_clientcached.get(name, &cc);
if(cc)
return cc;
ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = gamedef->getNodeDefManager();
IrrlichtDevice *device = tsrc->getDevice();
video::IVideoDriver *driver = device->getVideoDriver();
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->getTextureRaw(def->inventory_image);
}
else if(def->type == ITEM_NODE)
{
need_node_mesh = true;
}
// Create a wield mesh
if(cc->wield_mesh != NULL)
{
cc->wield_mesh->drop();
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->getTextureRaw(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);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
/*
Make a mesh from the node
*/
MeshMakeData mesh_make_data(gamedef);
MapNode mesh_make_node(id, param1, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data);
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
/*
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)
{
core::dimension2d<u32> dim(64,64);
std::string rtt_texture_name = "INVENTORY_"
+ def->name + "_RTT";
v3f camera_position(0, 1.0, -1.5);
camera_position.rotateXZBy(45);
v3f camera_lookat(0, 0, 0);
core::CMatrix4<f32> camera_projection_matrix;
// Set orthogonal projection
camera_projection_matrix.buildProjectionMatrixOrthoLH(
1.65, 1.65, 0, 100);
video::SColorf ambient_light(0.2,0.2,0.2);
v3f light_position(10, 100, -50);
video::SColorf light_color(0.5,0.5,0.5);
f32 light_radius = 1000;
cc->inventory_texture = generateTextureFromMesh(
node_mesh, device, dim, rtt_texture_name,
camera_position,
camera_lookat,
camera_projection_matrix,
ambient_light,
light_position,
light_color,
light_radius);
// render-to-target didn't work
if(cc->inventory_texture == NULL)
{
cc->inventory_texture =
tsrc->getTextureRaw(f.tiledef[0].name);
}
}
/*
Use the node mesh as the wield mesh
*/
if(cc->wield_mesh == NULL)
{
// Scale to proper wield mesh proportions
scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
* def->wield_scale);
cc->wield_mesh = node_mesh;
cc->wield_mesh->grab();
}
// falling outside of here deletes node_mesh
}
// Put in cache
m_clientcached.set(name, cc);
return cc;
}
ClientCached* getClientCached(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = NULL;
m_clientcached.get(name, &cc);
if(cc)
return cc;
if(get_current_thread_id() == m_main_thread)
{
return createClientCachedDirect(name, gamedef);
}
else
{
// We're gonna ask the result to be put into here
ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
// Throw a request in
m_get_clientcached_queue.add(name, 0, 0, &result_queue);
try{
// Wait result for a second
GetResult<std::string, ClientCached*, u8, u8>
result = result_queue.pop_front(1000);
// Check that at least something worked OK
assert(result.key == name);
// Return it
return result.item;
}
catch(ItemNotFoundException &e)
{
errorstream<<"Waiting for clientcached timed out."<<std::endl;
return &m_dummy_clientcached;
}
}
}
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = getClientCached(name, gamedef);
if(!cc)
return NULL;
return cc->inventory_texture;
}
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = getClientCached(name, gamedef);
if(!cc)
return NULL;
return cc->wield_mesh;
}
#endif
void clear()
{
for(std::map<std::string, ItemDefinition*>::const_iterator
@ -321,157 +542,6 @@ public:
m_aliases[name] = convert_to;
}
}
virtual void updateTexturesAndMeshes(IGameDef *gamedef)
{
#ifndef SERVER
infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating "
<<"textures and meshes in item definitions"<<std::endl;
ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = gamedef->getNodeDefManager();
IrrlichtDevice *device = tsrc->getDevice();
video::IVideoDriver *driver = device->getVideoDriver();
for(std::map<std::string, ItemDefinition*>::iterator
i = m_item_definitions.begin();
i != m_item_definitions.end(); i++)
{
ItemDefinition *def = i->second;
bool need_node_mesh = false;
// Create an inventory texture
def->inventory_texture = NULL;
if(def->inventory_image != "")
{
def->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
}
else if(def->type == ITEM_NODE)
{
need_node_mesh = true;
}
// Create a wield mesh
if(def->wield_mesh != NULL)
{
def->wield_mesh->drop();
def->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;
def->wield_mesh = createExtrudedMesh(
tsrc->getTextureRaw(imagename),
driver,
def->wield_scale * v3f(40.0, 40.0, 4.0));
if(def->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);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
/*
Make a mesh from the node
*/
MeshMakeData mesh_make_data(gamedef);
MapNode mesh_make_node(id, param1, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data);
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
/*
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(def->inventory_texture == NULL)
{
core::dimension2d<u32> dim(64,64);
std::string rtt_texture_name = "INVENTORY_"
+ def->name + "_RTT";
v3f camera_position(0, 1.0, -1.5);
camera_position.rotateXZBy(45);
v3f camera_lookat(0, 0, 0);
core::CMatrix4<f32> camera_projection_matrix;
// Set orthogonal projection
camera_projection_matrix.buildProjectionMatrixOrthoLH(
1.65, 1.65, 0, 100);
video::SColorf ambient_light(0.2,0.2,0.2);
v3f light_position(10, 100, -50);
video::SColorf light_color(0.5,0.5,0.5);
f32 light_radius = 1000;
def->inventory_texture = generateTextureFromMesh(
node_mesh, device, dim, rtt_texture_name,
camera_position,
camera_lookat,
camera_projection_matrix,
ambient_light,
light_position,
light_color,
light_radius);
// render-to-target didn't work
if(def->inventory_texture == NULL)
{
def->inventory_texture =
tsrc->getTextureRaw(f.tiledef[0].name);
}
}
/*
Use the node mesh as the wield mesh
*/
if(def->wield_mesh == NULL)
{
// Scale to proper wield mesh proportions
scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
* def->wield_scale);
def->wield_mesh = node_mesh;
def->wield_mesh->grab();
}
// falling outside of here deletes node_mesh
}
}
#endif
}
void serialize(std::ostream &os)
{
writeU8(os, 0); // version
@ -521,11 +591,37 @@ public:
registerAlias(name, convert_to);
}
}
void processQueue(IGameDef *gamedef)
{
#ifndef SERVER
while(m_get_clientcached_queue.size() > 0)
{
GetRequest<std::string, ClientCached*, u8, u8>
request = m_get_clientcached_queue.pop();
GetResult<std::string, ClientCached*, u8, u8>
result;
result.key = request.key;
result.callers = request.callers;
result.item = createClientCachedDirect(request.key, gamedef);
request.dest->push_back(result);
}
#endif
}
private:
// Key is name
std::map<std::string, ItemDefinition*> m_item_definitions;
// Aliases
std::map<std::string, std::string> m_aliases;
#ifndef SERVER
// The id of the thread that is allowed to use irrlicht directly
threadid_t m_main_thread;
// A reference to this can be returned when nothing is found, to avoid NULLs
mutable ClientCached m_dummy_clientcached;
// Cached textures and meshes
mutable MutexedMap<std::string, ClientCached*> m_clientcached;
// Queued clientcached fetches (to be processed by the main thread)
mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
#endif
};
IWritableItemDefManager* createItemDefManager()

@ -72,14 +72,6 @@ struct ItemDefinition
// "" = no prediction
std::string node_placement_prediction;
/*
Cached stuff
*/
#ifndef SERVER
video::ITexture *inventory_texture;
scene::IMesh *wield_mesh;
#endif
/*
Some helpful methods
*/
@ -108,6 +100,14 @@ public:
virtual std::set<std::string> getAll() const=0;
// Check if item is known
virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const=0;
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const=0;
#endif
virtual void serialize(std::ostream &os)=0;
};
@ -126,6 +126,14 @@ public:
virtual std::set<std::string> getAll() const=0;
// Check if item is known
virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const=0;
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const=0;
#endif
// Remove all registered item and node definitions and aliases
// Then re-add the builtin item definitions
@ -138,15 +146,11 @@ public:
virtual void registerAlias(const std::string &name,
const std::string &convert_to)=0;
/*
Update inventory textures and wield meshes to latest
return values of ITextureSource and INodeDefManager.
Call after updating the texture atlas of a texture source.
*/
virtual void updateTexturesAndMeshes(IGameDef *gamedef)=0;
virtual void serialize(std::ostream &os)=0;
virtual void deSerialize(std::istream &is)=0;
// Do stuff asked by threads that can only be done in the main thread
virtual void processQueue(IGameDef *gamedef)=0;
};
IWritableItemDefManager* createItemDefManager();

@ -107,6 +107,17 @@ public:
return true;
}
core::list<Value> getValues()
{
core::list<Value> result;
for(typename core::map<Key, Value>::Iterator
i = m_values.getIterator();
i.atEnd() == false; i++){
result.push_back(i.getNode()->getValue());
}
return result;
}
private:
core::map<Key, Value> m_values;
JMutex m_mutex;