Ported all the demos and loaders across to use the new multitexturing API.
This commit is contained in:
@@ -12,7 +12,8 @@
|
||||
namespace osgUtil {
|
||||
|
||||
/**
|
||||
* RenderBin base class.
|
||||
* RenderStage which copies the final image to an attached texture or image.
|
||||
* Generally used as a pre-rendering stage.
|
||||
*/
|
||||
class OSGUTIL_EXPORT RenderToTextureStage : public RenderStage
|
||||
{
|
||||
@@ -33,6 +34,9 @@ class OSGUTIL_EXPORT RenderToTextureStage : public RenderStage
|
||||
void setTexture(osg::Texture* texture) { _texture = texture; }
|
||||
osg::Texture* getTexture() { return _texture.get(); }
|
||||
|
||||
void setImage(osg::Image* image) { _image = image; }
|
||||
osg::Image* getImage() { return _image.get(); }
|
||||
|
||||
virtual void draw(osg::State& state,RenderLeaf*& previous);
|
||||
|
||||
public:
|
||||
@@ -43,6 +47,7 @@ class OSGUTIL_EXPORT RenderToTextureStage : public RenderStage
|
||||
virtual ~RenderToTextureStage();
|
||||
|
||||
osg::ref_ptr<osg::Texture> _texture;
|
||||
osg::ref_ptr<osg::Image> _image;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ Node *makeBase( void )
|
||||
|
||||
StateSet *dstate = new StateSet;
|
||||
dstate->setMode( GL_LIGHTING, StateAttribute::OFF );
|
||||
dstate->setAttributeAndModes( tex, StateAttribute::ON );
|
||||
dstate->setTextureAttributeAndModes(0, tex, StateAttribute::ON );
|
||||
|
||||
dstate->setAttribute( new TexEnv );
|
||||
dstate->setTextureAttribute(0, new TexEnv );
|
||||
|
||||
// clear the depth to the far plane.
|
||||
osg::Depth* depth = new osg::Depth;
|
||||
|
||||
@@ -104,8 +104,8 @@ Node *makeSky( void )
|
||||
|
||||
StateSet *dstate = new StateSet;
|
||||
|
||||
dstate->setAttributeAndModes( tex, StateAttribute::OFF );
|
||||
dstate->setAttribute( new TexEnv );
|
||||
dstate->setTextureAttributeAndModes(0, tex, StateAttribute::OFF );
|
||||
dstate->setTextureAttribute(0, new TexEnv );
|
||||
dstate->setMode( GL_LIGHTING, StateAttribute::OFF );
|
||||
dstate->setMode( GL_CULL_FACE, StateAttribute::ON );
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ Node *makeTank( void )
|
||||
tex->setImage(osgDB::readImageFile("Images/tank.rgb"));
|
||||
|
||||
StateSet *dstate = new StateSet;
|
||||
dstate->setAttributeAndModes( tex, StateAttribute::ON );
|
||||
dstate->setAttribute( new TexEnv );
|
||||
dstate->setTextureAttributeAndModes(0, tex, StateAttribute::ON );
|
||||
dstate->setTextureAttribute(0, new TexEnv );
|
||||
|
||||
gset->setStateSet( dstate );
|
||||
geode->addDrawable( gset );
|
||||
|
||||
@@ -117,8 +117,8 @@ Node *makeTerrain( void )
|
||||
|
||||
StateSet *dstate = new StateSet;
|
||||
dstate->setMode( GL_LIGHTING, StateAttribute::OFF );
|
||||
dstate->setAttributeAndModes( tex, StateAttribute::ON );
|
||||
dstate->setAttribute( new TexEnv );
|
||||
dstate->setTextureAttributeAndModes(0, tex, StateAttribute::ON );
|
||||
dstate->setTextureAttribute(0, new TexEnv );
|
||||
|
||||
geom->setStateSet( dstate );
|
||||
|
||||
|
||||
@@ -221,8 +221,8 @@ Node *makeTrees( void )
|
||||
|
||||
StateSet *dstate = new StateSet;
|
||||
|
||||
dstate->setAttributeAndModes( tex, StateAttribute::ON );
|
||||
dstate->setAttribute( new TexEnv );
|
||||
dstate->setTextureAttributeAndModes(0, tex, StateAttribute::ON );
|
||||
dstate->setTextureAttribute(0, new TexEnv );
|
||||
|
||||
dstate->setAttributeAndModes( new Transparency, StateAttribute::ON );
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ osg::Node* createTexturedItem(const osg::Vec3& offset,osg::Texture* texture,osg:
|
||||
// create the StateSet to store the texture data
|
||||
osg::StateSet* stateset = osgNew osg::StateSet;
|
||||
|
||||
stateset->setAttributeAndModes(texture,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
|
||||
|
||||
// turn the face culling off so you can see the texture from
|
||||
// all angles.
|
||||
|
||||
@@ -53,7 +53,7 @@ osg::StateSet* createMirrorTexturedState(const std::string& filename)
|
||||
{
|
||||
osg::Texture* texture = new osg::Texture;
|
||||
texture->setImage(image);
|
||||
dstate->setAttributeAndModes(texture,osg::StateAttribute::ON);
|
||||
dstate->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
return dstate;
|
||||
|
||||
@@ -139,7 +139,7 @@ osg::Node* createTexturedItem(const osg::Vec3& offset,osg::Texture* texture,osg:
|
||||
// create the StateSet to store the texture data
|
||||
osg::StateSet* stateset = new osg::StateSet;
|
||||
|
||||
stateset->setAttributeAndModes(texture,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
|
||||
|
||||
// turn the face culling off so you can see the texture from
|
||||
// all angles.
|
||||
|
||||
@@ -488,7 +488,7 @@ Geode* osg::createGeodeForImage(osg::Image* image,const float s,const float t)
|
||||
osg::StateSet* dstate = osgNew osg::StateSet;
|
||||
dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
|
||||
dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
||||
dstate->setAttributeAndModes(texture,osg::StateAttribute::ON);
|
||||
dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);
|
||||
|
||||
// set up the geoset.
|
||||
Geometry* geom = osgNew Geometry;
|
||||
|
||||
@@ -65,12 +65,11 @@ public:
|
||||
}
|
||||
osg::TexEnv* texenv = new osg::TexEnv;
|
||||
texenv->setMode(osg::TexEnv::MODULATE);
|
||||
dstate->setAttribute( texenv );
|
||||
dstate->setTextureAttribute(0, texenv );
|
||||
}
|
||||
}
|
||||
if (ctx && tx) { // texture exists
|
||||
dstate->setAttributeAndModes(tx,osg::StateAttribute::ON);
|
||||
dstate->setMode(GL_TEXTURE_2D,StateAttribute::ON);
|
||||
dstate->setTextureAttributeAndModes(0,tx,osg::StateAttribute::ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ osg::StateSet* ReaderWriter3DS::createStateSet(Lib3dsMaterial *mat)
|
||||
osg::Texture* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy);
|
||||
if (texture1_map)
|
||||
{
|
||||
stateset->setAttributeAndModes(texture1_map,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
|
||||
|
||||
// not sure exactly how to interpret what is best for .3ds
|
||||
// but the default text env MODULATE doesn't work well, and
|
||||
@@ -707,7 +707,7 @@ osg::StateSet* ReaderWriter3DS::createStateSet(Lib3dsMaterial *mat)
|
||||
texenv->setMode(osg::TexEnv::MODULATE);
|
||||
}
|
||||
|
||||
stateset->setAttribute(texenv);
|
||||
stateset->setTextureAttribute(0,texenv);
|
||||
}
|
||||
|
||||
if (transparency>0.0f || textureTransparancy)
|
||||
|
||||
@@ -147,7 +147,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode(const std::string& fil
|
||||
osg::Texture* texture = new osg::Texture;
|
||||
texture->setImage(image);
|
||||
|
||||
stateset->setAttributeAndModes(texture,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
|
||||
gc._texturesActive=true;
|
||||
|
||||
gc._geom->setStateSet(stateset);
|
||||
|
||||
@@ -132,13 +132,13 @@ osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(const std::string& fil
|
||||
{
|
||||
osg::Texture* osg_texture = new osg::Texture;
|
||||
osg_texture->setImage(osg_image);
|
||||
stateset->setAttributeAndModes(osg_texture,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,osg_texture,osg::StateAttribute::ON);
|
||||
|
||||
if (omtl->textureReflection)
|
||||
{
|
||||
osg::TexGen* osg_texgen = new osg::TexGen;
|
||||
osg_texgen->setMode(osg::TexGen::SPHERE_MAP);
|
||||
stateset->setAttributeAndModes(osg_texgen,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(0,osg_texgen,osg::StateAttribute::ON);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -46,11 +46,13 @@ RegisterDotOsgWrapperProxy g_GeoStateFuncProxy
|
||||
//
|
||||
// Set up the maps from name to GLMode and visa versa.
|
||||
//
|
||||
typedef std::map<std::string,StateAttribute::GLMode> GLNameToGLModeMap;
|
||||
typedef std::map<StateAttribute::GLMode,std::string> GLModeToGLNameMap;
|
||||
typedef std::map<std::string,StateAttribute::GLMode> GLNameToGLModeMap;
|
||||
typedef std::map<StateAttribute::GLMode,std::string> GLModeToGLNameMap;
|
||||
typedef std::set<StateAttribute::GLMode> TextureGLModeSet;
|
||||
|
||||
GLNameToGLModeMap s_GLNameToGLModeMap;
|
||||
GLModeToGLNameMap s_GLModeToGLNameMap;
|
||||
TextureGLModeSet s_TextureGLModeSet;
|
||||
|
||||
#define ADD_NAME(name,mode) s_GLNameToGLModeMap[name]=mode; s_GLModeToGLNameMap[mode]=name;
|
||||
|
||||
@@ -100,6 +102,19 @@ void initGLNames()
|
||||
ADD_NAME("GL_LIGHT5",GL_LIGHT5);
|
||||
ADD_NAME("GL_LIGHT6",GL_LIGHT6);
|
||||
ADD_NAME("GL_LIGHT7",GL_LIGHT7);
|
||||
|
||||
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_1D);
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_2D);
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_3D);
|
||||
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_CUBE_MAP);
|
||||
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_GEN_Q);
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_GEN_R);
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_GEN_S);
|
||||
s_TextureGLModeSet.insert(GL_TEXTURE_GEN_T);
|
||||
|
||||
|
||||
// for(GLNameToGLModeMap::iterator itr=s_GLNameToGLModeMap.begin();
|
||||
// itr!=s_GLNameToGLModeMap.end();
|
||||
@@ -159,7 +174,7 @@ bool GeoState_readLocalData(Object& obj, Input& fr)
|
||||
|
||||
if (fr[0].matchWord("texturing") && StateSet_matchModeStr(fr[1].getStr(),mode))
|
||||
{
|
||||
statset.setMode(GL_TEXTURE_2D,mode);
|
||||
statset.setTextureMode(0,GL_TEXTURE_2D,mode);
|
||||
fr+=2;
|
||||
iteratorAdvanced = true;
|
||||
}
|
||||
@@ -218,7 +233,15 @@ bool GeoState_readLocalData(Object& obj, Input& fr)
|
||||
StateAttribute* attribute = NULL;
|
||||
while((attribute=fr.readStateAttribute())!=NULL)
|
||||
{
|
||||
statset.setAttribute(attribute);
|
||||
if (attribute->isTextureAttribute())
|
||||
{
|
||||
// remap to be a texture attribute
|
||||
statset.setTextureAttribute(0,attribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
statset.setAttribute(attribute);
|
||||
}
|
||||
|
||||
if (attribute->getType()==StateAttribute::TEXGEN)
|
||||
statset.setAssociatedModes(attribute,texgening);
|
||||
@@ -314,7 +337,16 @@ bool StateSet_readLocalData(Object& obj, Input& fr)
|
||||
{
|
||||
int mode;
|
||||
fr[0].getInt(mode);
|
||||
stateset.setMode((StateAttribute::GLMode)mode,value);
|
||||
|
||||
if (s_TextureGLModeSet.find(mode)!=s_TextureGLModeSet.end())
|
||||
{
|
||||
// remap to a texture unit.
|
||||
stateset.setTextureMode(0,(StateAttribute::GLMode)mode,value);
|
||||
}
|
||||
else
|
||||
{
|
||||
stateset.setMode((StateAttribute::GLMode)mode,value);
|
||||
}
|
||||
fr+=2;
|
||||
iteratorAdvanced = true;
|
||||
readingMode=true;
|
||||
@@ -329,7 +361,15 @@ bool StateSet_readLocalData(Object& obj, Input& fr)
|
||||
if (nitr!=s_GLNameToGLModeMap.end())
|
||||
{
|
||||
StateAttribute::GLMode mode = nitr->second;
|
||||
stateset.setMode(mode,value);
|
||||
if (s_TextureGLModeSet.find(mode)!=s_TextureGLModeSet.end())
|
||||
{
|
||||
// remap to a texture unit.
|
||||
stateset.setTextureMode(0,mode,value);
|
||||
}
|
||||
else
|
||||
{
|
||||
stateset.setMode(mode,value);
|
||||
}
|
||||
fr+=2;
|
||||
iteratorAdvanced = true;
|
||||
readingMode=true;
|
||||
@@ -344,7 +384,15 @@ bool StateSet_readLocalData(Object& obj, Input& fr)
|
||||
StateAttribute* attribute = NULL;
|
||||
while((attribute=fr.readStateAttribute())!=NULL)
|
||||
{
|
||||
stateset.setAttribute(attribute);
|
||||
if (attribute->isTextureAttribute())
|
||||
{
|
||||
// remap to be a texture attribute
|
||||
stateset.setTextureAttribute(0,attribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
stateset.setAttribute(attribute);
|
||||
}
|
||||
iteratorAdvanced = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -930,39 +930,39 @@ osg::StateSet* ConvertFromPerformer::visitGeoState(osg::Drawable* osgDrawable,pf
|
||||
{
|
||||
case(PFTG_OBJECT_LINEAR) :
|
||||
osgTexGen->setMode(osg::TexGen::OBJECT_LINEAR);
|
||||
osgStateSet->setAttribute(osgTexGen);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureAttribute(0,osgTexGen);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
break;
|
||||
case(PFTG_EYE_LINEAR_IDENT) :
|
||||
std::cerr << "TexGen Mode PFTG_EYE_LINEAR_IDENT not currently supported by the OSG,"<<std::endl;
|
||||
std::cerr << " assuming osg::TexGen::EYE_LINEAR."<<std::endl;
|
||||
case(PFTG_EYE_LINEAR) :
|
||||
osgTexGen->setMode(osg::TexGen::EYE_LINEAR);
|
||||
osgStateSet->setAttribute(osgTexGen);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureAttribute(0,osgTexGen);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
break;
|
||||
case(PFTG_SPHERE_MAP) :
|
||||
osgTexGen->setMode(osg::TexGen::SPHERE_MAP);
|
||||
osgStateSet->setAttribute(osgTexGen);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setMode(GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureAttribute(0,osgTexGen);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
osgStateSet->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
break;
|
||||
case(PFTG_OFF) :
|
||||
osgTexGen->setStateSetModes(*osgStateSet,osg::StateAttribute::OFF);
|
||||
osgStateSet->setAssociatedTextureModes(0,osgTexGen,osg::StateAttribute::OFF);
|
||||
break;
|
||||
case(PFTG_OBJECT_DISTANCE_TO_LINE) :
|
||||
std::cerr << "TexGen Mode PFTG_OBJECT_DISTANCE_TO_LINE not currently supported by the OSG."<<std::endl;
|
||||
osgTexGen->setStateSetModes(*osgStateSet,osg::StateAttribute::OFF);
|
||||
osgStateSet->setAssociatedTextureModes(0,osgTexGen,osg::StateAttribute::OFF);
|
||||
break;
|
||||
case(PFTG_EYE_DISTANCE_TO_LINE) :
|
||||
std::cerr << "TexGen Mode PFTG_EYE_DISTANCE_TO_LINE not currently supported by the OSG."<<std::endl;
|
||||
osgTexGen->setStateSetModes(*osgStateSet,osg::StateAttribute::OFF);
|
||||
osgStateSet->setAssociatedTextureModes(0,osgTexGen,osg::StateAttribute::OFF);
|
||||
break;
|
||||
default:
|
||||
std::cerr << "TexGen Mode "<<mode<<" not currently supported by the OSG."<<std::endl;
|
||||
osgTexGen->setStateSetModes(*osgStateSet,osg::StateAttribute::OFF);
|
||||
osgStateSet->setAssociatedTextureModes(0,osgTexGen,osg::StateAttribute::OFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -977,7 +977,7 @@ osg::StateSet* ConvertFromPerformer::visitGeoState(osg::Drawable* osgDrawable,pf
|
||||
|
||||
osg::TexMat* osgTexMat = new osg::TexMat();
|
||||
osgTexMat->setMatrix(osgMatrix);
|
||||
osgStateSet->setAttribute(osgTexMat);
|
||||
osgStateSet->setTextureAttribute(0,osgTexMat);
|
||||
}
|
||||
|
||||
return osgStateSet;
|
||||
@@ -1123,7 +1123,7 @@ osg::Texture* ConvertFromPerformer::visitTexture(osg::StateSet* osgStateSet,pfTe
|
||||
|
||||
osg::Texture* osgTexture = dynamic_cast<osg::Texture*>(getOsgObject(tex));
|
||||
if (osgTexture) {
|
||||
if (osgStateSet) osgStateSet->setAttribute(osgTexture);
|
||||
if (osgStateSet) osgStateSet->setTextureAttribute(0,osgTexture);
|
||||
return osgTexture;
|
||||
}
|
||||
|
||||
@@ -1131,7 +1131,7 @@ osg::Texture* ConvertFromPerformer::visitTexture(osg::StateSet* osgStateSet,pfTe
|
||||
registerPfObjectForOsgObject(tex, osgTexture);
|
||||
//_pfToOsgMap[tex] = osgTexture;
|
||||
|
||||
if (osgStateSet) osgStateSet->setAttribute(osgTexture);
|
||||
if (osgStateSet) osgStateSet->setTextureAttribute(0,osgTexture);
|
||||
|
||||
int repeat_r = tex->getRepeat(PFTEX_WRAP_R);
|
||||
int repeat_s = tex->getRepeat(PFTEX_WRAP_S);
|
||||
|
||||
@@ -201,7 +201,7 @@ void TrPageArchive::LoadMaterials()
|
||||
break;
|
||||
}
|
||||
|
||||
osg_state_set->setAttribute(osg_texenv);
|
||||
osg_state_set->setTextureAttribute(0,osg_texenv);
|
||||
|
||||
Material *osg_material = new Material;
|
||||
|
||||
@@ -255,7 +255,7 @@ void TrPageArchive::LoadMaterials()
|
||||
{
|
||||
osg_texture->setWrap(Texture::WRAP_S, wrap_s == trpgTextureEnv::Repeat ? Texture::REPEAT: Texture::CLAMP );
|
||||
osg_texture->setWrap(Texture::WRAP_T, wrap_t == trpgTextureEnv::Repeat ? Texture::REPEAT: Texture::CLAMP );
|
||||
osg_state_set->setAttributeAndModes(osg_texture, StateAttribute::ON);
|
||||
osg_state_set->setTextureAttributeAndModes(0,osg_texture, StateAttribute::ON);
|
||||
|
||||
if(osg_texture->getImage())
|
||||
{
|
||||
|
||||
@@ -672,7 +672,7 @@ void TrPageParser::LoadLocalMaterials()
|
||||
break;
|
||||
}
|
||||
|
||||
osg_state_set->setAttribute(osg_texenv);
|
||||
osg_state_set->setTextureAttribute(0,osg_texenv);
|
||||
|
||||
Texture* osg_texture = GetLocalTexture(image_helper,&locmat, tex);
|
||||
|
||||
@@ -687,7 +687,7 @@ void TrPageParser::LoadLocalMaterials()
|
||||
osg_state_set->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
}
|
||||
osg_state_set->setAttributeAndModes(osg_texture, StateAttribute::ON);
|
||||
osg_state_set->setTextureAttributeAndModes(0,osg_texture, StateAttribute::ON);
|
||||
|
||||
int wrap_s, wrap_t;
|
||||
texEnv.GetWrap(wrap_s, wrap_t);
|
||||
|
||||
@@ -30,4 +30,7 @@ void RenderToTextureStage::draw(osg::State& state,RenderLeaf*& previous)
|
||||
if (_texture.valid())
|
||||
_texture->copyTexImage2D(state,_viewport->x(),_viewport->y(),_viewport->width(),_viewport->height());
|
||||
|
||||
if (_image.valid())
|
||||
_image->readPixels(_viewport->x(),_viewport->y(),_viewport->width(),_viewport->height(),GL_RGBA,GL_UNSIGNED_BYTE);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user