Render Target for J2D
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m10s
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m10s
This commit is contained in:
@@ -80,16 +80,16 @@ endif()
|
||||
|
||||
set_target_properties(JGL PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
#Don't expose this one because it's only to be used in the demo program.
|
||||
include_directories(${ReTexture_SOURCE_DIR}/include)
|
||||
#Don't expose these ones.
|
||||
include_directories(${ReWindow_SOURCE_DIR}/include)
|
||||
|
||||
target_include_directories(JGL PUBLIC
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${OPENGL_INCLUDE_DIRS}
|
||||
${ReTexture_SOURCE_DIR}/include
|
||||
${mcolor_SOURCE_DIR}/include
|
||||
${J3ML_SOURCE_DIR}/include
|
||||
${Event_SOURCE_DIR}/include
|
||||
${ReWindow_SOURCE_DIR}/include
|
||||
${glad_SOURCE_DIR}/include
|
||||
${jlog_SOURCE_DIR}/include
|
||||
)
|
||||
@@ -100,13 +100,13 @@ add_executable(JGL_Demo main.cpp)
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS})
|
||||
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
|
||||
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML ReWindowLibrary glad jlog Event ReTexture)
|
||||
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog Event ReTexture)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_include_directories(JGL PRIVATE ${freetype_SOURCE_DIR}/include)
|
||||
target_link_libraries(JGL PRIVATE freetype)
|
||||
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML ReWindowLibrary glad jlog Event ReTexture)
|
||||
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog Event ReTexture)
|
||||
endif()
|
||||
|
||||
target_link_libraries(JGL_Demo PUBLIC JGL)
|
||||
target_link_libraries(JGL_Demo PUBLIC JGL ReWindowLibrary)
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <JGL/types/Enums.h>
|
||||
#include <JGL/types/FontCache.h>
|
||||
#include <JGL/types/Font.h>
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
@@ -41,7 +42,7 @@ namespace JGL {
|
||||
/// @note This call may not strictly be necessary on some setups, but is provided to keep the API constant.
|
||||
/// It is recommended to always open a JGL 2D context to render your content, then close when completed.
|
||||
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls.
|
||||
void Begin();
|
||||
void Begin(RenderTarget* render_target = nullptr, bool clear_buffers = false);
|
||||
/// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
|
||||
/// @see Begin().
|
||||
void End();
|
||||
@@ -76,6 +77,8 @@ namespace JGL {
|
||||
/// Draws a filled rectangle with rounded corners on the screen.
|
||||
void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
|
||||
|
||||
void DrawRenderTargetAsSprite(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
|
||||
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
|
||||
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
|
||||
/// @param texture
|
||||
/// @param position
|
||||
|
@@ -10,16 +10,26 @@ namespace JGL {
|
||||
|
||||
class JGL::RenderTarget {
|
||||
private:
|
||||
Color4 background_color = {0,0,0,0};
|
||||
Color4 clear_color{0,0,0,0};
|
||||
bool using_depth = false;
|
||||
GLuint framebuffer_object = 0;
|
||||
GLuint depth_buffer = 0;
|
||||
Texture texture;
|
||||
Texture* texture = nullptr;
|
||||
public:
|
||||
static GLuint GetActiveGLRenderTargetHandle();
|
||||
static GLuint GetActiveGLFramebufferHandle();
|
||||
static void SetActiveGLRenderTarget(const RenderTarget& render_target);
|
||||
public:
|
||||
[[nodiscard]] Texture GetGLTexture();
|
||||
[[nodiscard]] GLuint GetGLTextureHandle();
|
||||
[[nodiscard]] GLuint GetGLFramebufferObjectHandle();
|
||||
[[nodiscard]] GLuint GetGLDepthBufferHandle();
|
||||
[[nodiscard]] Vector2 GetDimensions() const;
|
||||
[[nodiscard]] Texture* GetJGLTexture() const;
|
||||
[[nodiscard]] GLuint GetGLTextureHandle() const;
|
||||
[[nodiscard]] GLuint GetGLFramebufferObjectHandle() const;
|
||||
[[nodiscard]] GLuint GetGLDepthBufferHandle() const;
|
||||
[[nodiscard]] Color4 GetClearColor() const;
|
||||
public:
|
||||
/// Create a render target for a texture that already exists. For decals.
|
||||
explicit RenderTarget(const Texture& texture, const Color4& clear_color = Colors::Black, bool use_depth = false);
|
||||
/// Create a Render Target with a brand new texture. Want to render JGL elements onto a texture and display it as a sprite?
|
||||
explicit RenderTarget(unsigned int size, const Color4& clear_color = Colors::Black, bool use_depth = false);
|
||||
|
||||
void Erase();
|
||||
};
|
@@ -40,8 +40,6 @@ namespace JGL {
|
||||
/* Initialize a texture filled with trash data
|
||||
this is primarily for the RenderTarget */
|
||||
explicit Texture(const Vector2& size);
|
||||
/* Initialize a texture that is a single color */
|
||||
Texture(const Color4& color, const Vector2& size);
|
||||
Texture() = default;
|
||||
public:
|
||||
[[nodiscard]] GLuint GetGLTextureHandle() const;
|
||||
|
18
main.cpp
18
main.cpp
@@ -4,9 +4,6 @@
|
||||
#include <Colors.hpp>
|
||||
#include <chrono>
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <JGL/types/Font.h>
|
||||
#include <JGL/Logger.h>
|
||||
#include <ReTexture/Texture.h>
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
using namespace JGL;
|
||||
@@ -51,8 +48,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Texture* image;
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Vector3 position = {0,0,0};
|
||||
@@ -96,6 +91,9 @@ Gizmo b({200, 250});
|
||||
Gizmo c({350, 300});
|
||||
Gizmo d({450, 250});
|
||||
|
||||
Texture* image;
|
||||
RenderTarget* j2d_render_target;
|
||||
|
||||
class JGLDemoWindow : public ReWindow::RWindow
|
||||
{
|
||||
public:
|
||||
@@ -114,6 +112,7 @@ public:
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR);
|
||||
j2d_render_target = new RenderTarget(1280, {0,0,0,0});
|
||||
}
|
||||
|
||||
Vector3 textAngle = {0,0,0};
|
||||
@@ -156,9 +155,9 @@ public:
|
||||
J3D::DrawString(Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f}, 1.f, 32, FreeSans, textAngle, true);
|
||||
J3D::End();
|
||||
|
||||
J2D::Begin();
|
||||
|
||||
J2D::Begin(j2d_render_target, true);
|
||||
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
|
||||
J2D::DrawSprite(*image, {300, 300}, 0, {0.5, 0.5}, {1, 1}, Colors::White);
|
||||
J2D::DrawMirrorSprite(*image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
|
||||
J2D::DrawPartialSprite(*image, {225, 300}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5});
|
||||
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
|
||||
@@ -193,6 +192,11 @@ public:
|
||||
d.Draw();
|
||||
J2D::End();
|
||||
|
||||
//Draw the Render Target that we just drew all that stuff onto.
|
||||
J2D::Begin();
|
||||
J2D::DrawRenderTargetAsSprite(*j2d_render_target, {0, 0});
|
||||
J2D::End();
|
||||
|
||||
if (framerate_measurement) {
|
||||
frames++;
|
||||
std::chrono::system_clock::time_point stop = std::chrono::high_resolution_clock::now();
|
||||
|
53
src/JGL.cpp
53
src/JGL.cpp
@@ -7,8 +7,13 @@
|
||||
#include <jlog/Logger.hpp>
|
||||
#include <J3ML/Algorithm/Bezier.hpp>
|
||||
|
||||
JGL::RenderTarget* render_target = nullptr;
|
||||
GLfloat oldColor[4] = {0, 0, 0, 1};
|
||||
GLfloat baseColor[4] = {1, 1, 1, 1};
|
||||
|
||||
GLuint current_fbo = 0;
|
||||
GLint viewport[4] = {0, 0, 0, 0};
|
||||
|
||||
bool inJ2D = false;
|
||||
bool inJ3D = false;
|
||||
bool wasTexture2DEnabled = false;
|
||||
@@ -30,12 +35,25 @@ namespace JGL {
|
||||
}
|
||||
|
||||
#pragma region J2D
|
||||
void J2D::Begin() {
|
||||
void J2D::Begin(RenderTarget* rt, bool clear_buffers) {
|
||||
GLfloat old_clear_color[4];
|
||||
if (rt != nullptr) {
|
||||
render_target = rt;
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, old_clear_color);
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
current_fbo = JGL::RenderTarget::GetActiveGLFramebufferHandle();
|
||||
JGL::RenderTarget::SetActiveGLRenderTarget(*rt);
|
||||
}
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glViewport(0, 0, (int) wS.x, (int) wS.y);
|
||||
glOrtho(0, wS.x, wS.y, 0, -1, 1);
|
||||
|
||||
if (rt == nullptr)
|
||||
glViewport(0, 0, (int) wS.x, (int) wS.y),
|
||||
glOrtho(0, wS.x, wS.y, 0, -1, 1);
|
||||
else
|
||||
glOrtho(0, rt->GetDimensions().x, rt->GetDimensions().y, 0, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
@@ -97,6 +115,12 @@ namespace JGL {
|
||||
if (!inJ3D)
|
||||
inJ2D = true;
|
||||
else { jlog::Error("Attempt to Begin J2D inside of J3D context."); }
|
||||
|
||||
if (rt != nullptr && clear_buffers) {
|
||||
glClearColor(rt->GetClearColor().R(), rt->GetClearColor().G(), rt->GetClearColor().B(), rt->GetClearColor().A());
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glClearColor(old_clear_color[0], old_clear_color[1], old_clear_color[2], old_clear_color[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void J2D::End() {
|
||||
@@ -132,6 +156,11 @@ namespace JGL {
|
||||
//Put the draw color back how it was before.
|
||||
glColor4fv(oldColor);
|
||||
|
||||
if (render_target != nullptr) {
|
||||
render_target = nullptr;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
}
|
||||
inJ2D = false;
|
||||
}
|
||||
|
||||
@@ -259,6 +288,24 @@ namespace JGL {
|
||||
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + size.y - radius}, radius, subdivisions);
|
||||
}
|
||||
|
||||
void
|
||||
J2D::DrawRenderTargetAsSprite(const JGL::RenderTarget& rt, const Vector2& position, float rad_rotation, const Vector2& origin,
|
||||
const Vector2& scale, const Color4& color, Direction inversion) {
|
||||
|
||||
//Correct for the render-target being upside-down.
|
||||
Direction d{};
|
||||
if (inversion == Direction::None)
|
||||
d = Direction::Vertical;
|
||||
else if (inversion == Direction::Horizontal)
|
||||
d = Direction::Horizontal | Direction::Vertical;
|
||||
else if (inversion& Direction::Horizontal && inversion& Direction::Vertical)
|
||||
d = Direction::Horizontal;
|
||||
|
||||
//Change the blending mode such that the alpha doesn't get multiplied again.
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
J2D::DrawSprite(*rt.GetJGLTexture(), position, rad_rotation, origin, scale, color, d);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin,
|
||||
const Vector2& scale, const Color4& color, Direction inversion) {
|
||||
if (!inJ2D)
|
||||
|
@@ -1,22 +1,24 @@
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
#include <jlog/Logger.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
JGL::Texture JGL::RenderTarget::GetGLTexture() {
|
||||
JGL::Texture* JGL::RenderTarget::GetJGLTexture() const {
|
||||
return texture;
|
||||
}
|
||||
|
||||
GLuint JGL::RenderTarget::GetGLTextureHandle() {
|
||||
return texture.GetGLTextureHandle();
|
||||
GLuint JGL::RenderTarget::GetGLTextureHandle() const {
|
||||
return texture->GetGLTextureHandle();
|
||||
}
|
||||
|
||||
GLuint JGL::RenderTarget::GetGLFramebufferObjectHandle() {
|
||||
GLuint JGL::RenderTarget::GetGLFramebufferObjectHandle() const {
|
||||
return framebuffer_object;
|
||||
}
|
||||
|
||||
GLuint JGL::RenderTarget::GetGLDepthBufferHandle() {
|
||||
GLuint JGL::RenderTarget::GetGLDepthBufferHandle() const {
|
||||
return depth_buffer;
|
||||
}
|
||||
|
||||
GLuint JGL::RenderTarget::GetActiveGLRenderTargetHandle() {
|
||||
GLuint JGL::RenderTarget::GetActiveGLFramebufferHandle() {
|
||||
GLuint fbo;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &fbo);
|
||||
return fbo;
|
||||
@@ -24,5 +26,62 @@ GLuint JGL::RenderTarget::GetActiveGLRenderTargetHandle() {
|
||||
|
||||
void JGL::RenderTarget::SetActiveGLRenderTarget(const RenderTarget& render_target) {
|
||||
RenderTarget rt = render_target;
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, rt.GetGLFramebufferObjectHandle());
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, rt.GetGLFramebufferObjectHandle());
|
||||
glViewport(0,0, render_target.GetJGLTexture()->GetDimensions().x, render_target.GetJGLTexture()->GetDimensions().x);
|
||||
}
|
||||
|
||||
Vector2 JGL::RenderTarget::GetDimensions() const {
|
||||
return texture->GetDimensions();
|
||||
}
|
||||
|
||||
void JGL::RenderTarget::Erase() {
|
||||
if (GetActiveGLFramebufferHandle() == framebuffer_object)
|
||||
jlog::Warning("Deleting the framebuffer that's currently in use?");
|
||||
|
||||
texture->Erase();
|
||||
|
||||
if (using_depth)
|
||||
glDeleteRenderbuffers(1, &depth_buffer);
|
||||
|
||||
glDeleteFramebuffers(1, &framebuffer_object);
|
||||
}
|
||||
|
||||
Color4 JGL::RenderTarget::GetClearColor() const {
|
||||
return clear_color;
|
||||
}
|
||||
|
||||
JGL::RenderTarget::RenderTarget(const unsigned int size, const Color4& clear_color, bool use_depth) {
|
||||
GLuint current_fbo = GetActiveGLFramebufferHandle();
|
||||
GLint viewport[4] = {0, 0, 0, 0};
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
texture = new Texture(Vector2(size, size));
|
||||
glGenFramebuffers(1, &framebuffer_object);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
|
||||
glViewport(0,0, size, size);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGLTextureHandle(), 0);
|
||||
|
||||
if (use_depth) {
|
||||
GLuint depthBuffer;
|
||||
glGenRenderbuffers(1, &depthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size, size);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
using_depth = true;
|
||||
}
|
||||
|
||||
GLfloat old_clear_color[4];
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, old_clear_color);
|
||||
glClearColor(clear_color.RedChannelNormalized(), clear_color.GreenChannelNormalized(), clear_color.BlueChannelNormalized(), clear_color.AlphaChannelNormalized());
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearColor(old_clear_color[0], old_clear_color[1], old_clear_color[2], old_clear_color[3]);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
throw std::runtime_error("Error " + std::to_string(status) + "while generating framebuffer");
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
this->clear_color = clear_color;
|
||||
}
|
||||
|
@@ -33,47 +33,15 @@ namespace JGL
|
||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||
}
|
||||
|
||||
Texture::Texture(const Color4& color, const Vector2& size) {
|
||||
GLuint previous_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
|
||||
|
||||
glGenTextures(1, &texture_handle);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_handle);
|
||||
|
||||
//Bilinear
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
//Clamp
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
auto* pixel_data = new std::vector<Color4>(size.x * size.y);
|
||||
|
||||
for (unsigned int i = 0; i < (unsigned int) (size.x * size.y); i++)
|
||||
pixel_data->at(i) = color;
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int) size.x, (int) size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data->data());
|
||||
delete pixel_data;
|
||||
|
||||
texture_format = TextureFormat::RGBA;
|
||||
texture_size = size;
|
||||
texture_filtering_mode = TextureFilteringMode::BILINEAR;
|
||||
texture_wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE;
|
||||
texture_flags = TextureFlag::NONE;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||
}
|
||||
|
||||
Texture::Texture(const Vector2& size) {
|
||||
GLuint previous_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
|
||||
|
||||
glGenTextures(1, &texture_handle);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_handle);
|
||||
|
||||
//Bilinear
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
//NEAREST
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
//Clamp
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
@@ -82,7 +50,7 @@ namespace JGL
|
||||
|
||||
texture_format = TextureFormat::RGBA;
|
||||
texture_size = size;
|
||||
texture_filtering_mode = TextureFilteringMode::BILINEAR;
|
||||
texture_filtering_mode = TextureFilteringMode::NEAREST;
|
||||
texture_wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE;
|
||||
texture_flags = TextureFlag::NONE;
|
||||
|
||||
|
Reference in New Issue
Block a user