I am using scene2d. Here is my code:
group.addActor(new Actor() {
#Override
public Actor hit(float arg0, float arg1) {
return null;
}
#Override
public void draw(SpriteBatch batch, float arg1) {
batch.end();
shapeRenderer.begin(ShapeType.FilledRectangle);
shapeRenderer.setColor(Color.RED);
shapeRenderer.filledRect(0, 0, 300, 20);
shapeRenderer.end();
batch.begin();
}
});
The problem is that it draws this rectangular relative to screen (x = 0, y = 0), but I need it to be drawn relative to my group. But if I draw other entities with:
batch.draw(texture, 0, 0, width, height);
it correctly draws at (x = 0, y = 0) relative my group (0,0 pixels from left-bottom corner of the group).
Any suggestions how can I implement shape drawing in scene2d? And can someone can explain why these two calls work differently?
ShapeRenderer has its own transform matrix and projection matrix. These are separate to those in the SpriteBatch that the scene2d Stage uses. If you update the ShapeRenderer's matrices to match those that scene2d is using when Actor.draw() is called then you should get the results that you want.
As Rod Hyde mentions, ShapeRenderer has its own transform matrix and projection matrix.
So you would have to get the SpriteBatch's projection Matrix first.
I am not sure if there is an elegant way to do it, I did it like this:
public class myActor extends Actor{
private ShapeRenderer shapeRenderer;
static private boolean projectionMatrixSet;
public myActor(){
shapeRenderer = new ShapeRenderer();
projectionMatrixSet = false;
}
#Override
public void draw(SpriteBatch batch, float alpha){
batch.end();
if(!projectionMatrixSet){
shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
}
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(Color.RED);
shapeRenderer.rect(0, 0, 50, 50);
shapeRenderer.end();
batch.begin();
}
}
The best solution for me. Cause when you using ShapeRenderer it's doesn't react on moving/zooming camera.
public class Rectangle extends Actor {
private Texture texture;
public Rectangle(float x, float y, float width, float height, Color color) {
createTexture((int)width, (int)height, color);
setX(x);
setY(y);
setWidth(width);
setHeight(height);
}
private void createTexture(int width, int height, Color color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
pixmap.fillRectangle(0, 0, width, height);
texture = new Texture(pixmap);
pixmap.dispose();
}
#Override
public void draw(Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
}
You need to convert the Actor's local coordinates into screen coordinates. Assuming your stage is full-screen, you can just use Actor.localToStageCoordinates:
vec.set(getX(), getY());
this.localToStageCoordinates(/* in/out */ vec);
shapeRenderer.filledRect(vec.x, vec.y, getWidth(), getHeight());
Where vec is a private Vector2d (you don't want to allocate a new one on each render call).
This is also assuming that your ShapeRenderer is defined to be map to the full screen (which is the default).
Also, if you switch away from the ShapeRenderer and back to the SpriteBatch, note that the batch is already adjusted to Actor coordinates (and thus you can use getX() and getY() directly with batch.draw(...).
If your are using the ShapeRenderer don't forget using setProjectionMatrix() and setTransformMatrix() methods...
A sample of draw circle inside an Actor on draw method :
#Override public void draw(Batch batch, float parentAlpha) {
batch.end();
if (shapeRenderer == null) {
shapeRenderer = new ShapeRenderer();
}
Gdx.gl.glEnable(GL20.GL_BLEND);
shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
shapeRenderer.setColor(mColor.r, mColor.g, mColor.b, mColor.a * parentAlpha);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.circle(getX() + getWidth()/2 , getY() + getHeight()/2 , Math.min(getWidth(),getHeight())/2 );
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
batch.begin();
}
Related
I have this constructor for a class that derives from QGraphicsRectItem:
Tower::Tower(int x, int y, QGraphicsScene *scene) : QGraphicsRectItem(x, y, width, height)
{
QBrush brush; // color it
brush.setStyle(Qt::SolidPattern);
brush.setColor(START_COLOR);
this->setBrush(brush);
this->setAcceptHoverEvents(true);
scene->addItem(this); // add to scene
}
And I have this code for a fire function which should create a bullet at the center of the Rectangle:
void Tower::fire()
{
Bullet *bullet = new Bullet(scenePos().x() + width / 2, scenePos().y() + height / 2, scene());
}
This is the code for Bullet:
Bullet::Bullet(int x, int y, QGraphicsScene *scene) : QGraphicsRectItem(x, y, width, height)
{
QBrush brush; // color it
brush.setStyle(Qt::SolidPattern);
brush.setColor(color);
this->setBrush(brush);
scene->addItem(this);
}
When the function runs it creates a bullet at the beginning of the scene.
What can I do to fix this?
I checked other questions but their answers didn't work for me.
In QGraphicsRectItem the position is not at the top left corner of the rectangle, but is the default item position (0, 0).
If you want the scene position of the center you can do something like this:
QPointF Tower::sceneCenter()
{
return mapToScene(rect().center());
}
I'm trying to use a lightmap to create an effect like this, but doing it only with blending functions and rendering: https://www.youtube.com/watch?v=JkyGTZ0992s
Basically, have the light map have 100% transparency when it's all white and have it fade off from there, as you can see in the video above.
Right now, it's blending, but it's very dark as you can see from the picture (ignore starbound art): https://gyazo.com/fa0cecbe6085db3dc15291a86396af13
Is this because of my blending modes? I can't figure it out, hopefully you can! Thanks!
CODE:
public ShaderProgram shader;
public Texture texture;
public Sprite sprite;
public Sprite lightSprite;
public Texture lightText;
public ShapeRenderer shape;
SpriteBatch spriteBatch;
#Override
public void create() {
shape = new ShapeRenderer();
spriteBatch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("image.png"));
sprite = new Sprite(texture);
lightText = new Texture(Gdx.files.internal("light.png"));
lightSprite = new Sprite(lightText);
ShaderProgram.pedantic = false;
shader = new ShaderProgram(Gdx.files.internal("shaders/passthrough.vert"), Gdx.files.internal("shaders/passthrough.frag"));
System.out.println(shader.getLog());
spriteBatch.setShader(shader);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
/*
Draw image
*/
spriteBatch.setShader(shader);
spriteBatch.begin();
sprite.draw(spriteBatch);
spriteBatch.setShader(null);
spriteBatch.setBlendFunction(GL30.GL_DST_COLOR, GL30.GL_SRC_ALPHA);
lightSprite.draw(spriteBatch, 1.0f);
spriteBatch.end();
lightSprite.setPosition(Gdx.input.getX(), Gdx.input.getY());
spriteBatch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
}
}`
The shader I'm using just makes the screen darker, here that is too:
VERT: http://pastebin.com/LkxCZGf6
FRAG: http://pastebin.com/dLwudnHx
lightmap:
Had asked a question here:
Drawing a portion of a hemisphere airspace
Probably it was more into GIS so have moved to more basic and specific implementation in OpenGL to get the desired output.
I have simply overridden/copied functions that are drawing the hemisphere and altering the GL part to basically INSERT clipping. I am able to draw the hemisphere centred at a location (latitude,longitude) and radius may be 2000. But when i cut it using a plane nothing happens. Please check the equation of the plane(its a plane parallel to the surface of the globe at height may be 1000, so (0,0,+1,1000))
The base class has drawUnitSphere so that might be causing some problems so trying to use GL's gluSphere(). But can't even see the sphere on globe. Used translate to shift it to my location(lat/lon) but still can't see it. Might be some issues with lat/lon and cartesian coordinates, or placing of my clipping code. Please check.
Here is the code:
#Override
public void drawSphere(DrawContext dc)
{
double[] altitudes = this.getAltitudes(dc.getVerticalExaggeration());
boolean[] terrainConformant = this.isTerrainConforming();
int subdivisions = this.getSubdivisions();
if (this.isEnableLevelOfDetail())
{
DetailLevel level = this.computeDetailLevel(dc);
Object o = level.getValue(SUBDIVISIONS);
if (o != null && o instanceof Integer)
subdivisions = (Integer) o;
}
Vec4 centerPoint = this.computePointFromPosition(dc,
this.location.getLatitude(), this.location.getLongitude(), altitudes[0], terrainConformant[0]);
Matrix modelview = dc.getView().getModelviewMatrix();
modelview = modelview.multiply(Matrix.fromTranslation(centerPoint));
modelview = modelview.multiply(Matrix.fromScale(this.getRadius()));
double[] matrixArray = new double[16];
modelview.toArray(matrixArray, 0, false);
this.setExpiryTime(-1L); // Sphere geometry never expires.
GL gl = dc.getGL(); // GL initialization checks for GL2 compatibility.
gl.glPushAttrib(GL.GL_POLYGON_BIT | GL.GL_TRANSFORM_BIT);
try
{
gl.glEnable(GL.GL_CULL_FACE);
gl.glFrontFace(GL.GL_CCW);
// Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
// before lighting is computed. In this case we're scaling by a constant factor, so GL_RESCALE_NORMAL
// is sufficient and potentially less expensive than GL_NORMALIZE (or computing unique normal vectors
// for each value of radius). GL_RESCALE_NORMAL was introduced in OpenGL version 1.2.
gl.glEnable(GL.GL_RESCALE_NORMAL);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glPushMatrix();
//clipping
DoubleBuffer eqn1 = BufferUtils.createDoubleBuffer(8).put(new double[] {0, 0, 1, 100});
eqn1.flip();
gl.glClipPlane(GL.GL_CLIP_PLANE0, eqn1);
gl.glEnable(GL.GL_CLIP_PLANE0);
try
{
gl.glLoadMatrixd(matrixArray, 0);
//this.drawUnitSphere(dc, subdivisions);
gl.glLoadIdentity();
gl.glTranslatef(75.2f, 32.5f, 0.0f);
gl.glColor3f(1.0f, 0.0f, 0.0f);
GLU glu = dc.getGLU();
GLUquadric qd=glu.gluNewQuadric();
glu.gluSphere(qd,3.0f,20,20);
}
finally
{
gl.glPopMatrix();
}
}
finally
{
gl.glPopAttrib();
}
}
#Override
public void drawUnitSphere(DrawContext dc, int subdivisions)
{
Object cacheKey = new Geometry.CacheKey(this.getClass(), "Sphere", subdivisions);
Geometry geom = (Geometry) this.getGeometryCache().getObject(cacheKey);
if (geom == null || this.isExpired(dc, geom))
{
if (geom == null)
geom = new Geometry();
this.makeSphere(1.0, subdivisions, geom);
this.updateExpiryCriteria(dc, geom);
this.getGeometryCache().add(cacheKey, geom);
}
this.getRenderer().drawGeometry(dc, geom);
}
#Override
public void makeSphere(double radius, int subdivisions, Geometry dest)
{
GeometryBuilder gb = this.getGeometryBuilder();
gb.setOrientation(GeometryBuilder.OUTSIDE);
GeometryBuilder.IndexedTriangleArray ita = gb.tessellateSphere((float) radius, subdivisions);
float[] normalArray = new float[3 * ita.getVertexCount()];
gb.makeIndexedTriangleArrayNormals(ita, normalArray);
dest.setElementData(GL.GL_TRIANGLES, ita.getIndexCount(), ita.getIndices());
dest.setVertexData(ita.getVertexCount(), ita.getVertices());
dest.setNormalData(ita.getVertexCount(), normalArray);
}
I want to render my scene to a texture and apply a blur shader to this texture .The problem is that when I draw back my texture the front faces of the cubes are invisible
without supersampling
with supersampling
*Ignore the opaque thing around the cube in both photos.I double render the cube once with less alpha and more scale ,I disabled this but I have the same problem.
For some reason I am using the y as z and z as y,so the front face of the cube has less y than the back face(instead of z) ,I am guessing something is wrong with the z-buffer.
The render to texture code:
public class RenderOnTexture {
private float m_fboScaler = 1f;
private boolean m_fboEnabled = true;
private FrameBuffer m_fbo = null;
private TextureRegion m_fboRegion = null;
public RenderOnTexture(float scale) {
int width = (int) (Gdx.graphics.getWidth()*scale);
int height = (int) (Gdx.graphics.getHeight()*scale);
m_fbo = new FrameBuffer(Format.RGB565, (int)(width * m_fboScaler), (int)(height * m_fboScaler), false);
m_fboRegion = new TextureRegion(m_fbo.getColorBufferTexture());
m_fboRegion.flip(false,false);
}
public void begin(){
if(m_fboEnabled)
{
m_fbo.begin();
Gdx.gl.glClearColor(0, 0,0,0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
}
public TextureRegion end(){
if(m_fbo != null)
{
m_fbo.end();
return m_fboRegion;
}
return null;
}
}
The Boolean argument in the FrameBuffer enables a depth buffer attachment. And the depth buffer must be cleared along with the color buffer.
I've three images which I'm trying to render with my SpriteBatch and FrameBuffer.
These are the steps I'm doing.
Clear the screen, depth and stencil buffers.
Bind the FrameBuffer. See FrameBuffer.bind().
Use my SpriteBatch to render three solid quads with different sizes and different positions.
Unbind my FrameBuffer. See FrameBuffer.unbind().
Bind my FrameBuffers colorTexture.
Render my FrameBuffer with my SpriteBatch.
Here's some facts I know about the this problem
The textures shows the right color. They are not white.
The first texture I render is shown, while the other two isn't. If I change the order, the first texture still gets rendered.
With three textures, I mean three separate texture ids.
I've depth test enabled.
No OpenGL error or FrameBuffer error is being thrown.
When rendering using my SpriteBatch directly to the screen all three quads is shown.
The size of my FrameBuffer is the size of my screen.
Here's how it's supposed to look like
Here's how it's looking with my framebuffer.
Here's how it's looking with my framebuffer and drawing the first texture two times. Still missing the other two textures. Position in not the problem.
Here's the code for my FrameBuffer :
// Stencil is broken currently broken, in my example. I'm not using any stencil buffers.
public FrameBuffer(int width, int height, boolean hasDepth, boolean hasStencil) {
this.width = width;
this.height = height;
frameBufferID = GL30.glGenFramebuffers();
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);
if (hasDepth && hasStencil) {
int depthAndStencilBufferID = GL30.glGenRenderbuffers();
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthAndStencilBufferID);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, width, height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthAndStencilBufferID);
}
else if (hasDepth) {
int depthBufferID = GL30.glGenRenderbuffers();
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthBufferID);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH_COMPONENT32F, width, height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthBufferID);
}
else if (hasStencil) {
int stencilBufferID = GL30.glGenRenderbuffers();
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, stencilBufferID);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_STENCIL_INDEX16, width, height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, stencilBufferID);
}
colorTexture = new Texture(width, height);
colorTexture.bind();
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, colorTexture.getTextureID(), 0);
int result = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
//error handling
RenderUtil.unbindFrameBuffer();
}
private Texture colorTexture;
private int width, height;
private int frameBufferID, depthBufferID, stencilBufferID;
public void begin () {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);
GL11.glViewport(0, 0, width, height);
}
public void end () {
GL11.glViewport(0, 0, RenderingEngine.getWidth(), RenderingEngine.getHeight());
RenderUtil.unbindFrameBuffer();
RenderUtil.unbindTextures();
}
public Texture getColorTexture () {
return colorTexture;
}
public int getWidth () {
return width;
}
public int getHeight () {
return height;
}
Here's how I create a Texture if it's to any use :
private void loadTexture (ByteBuffer buffer, int textureID, int width, int height) {
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, width, width, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
}
There seems to be a problem in my SpriteBatch when switching texture. Here's how the switching works.
I remind you, using my SpriteBatch to render to the screen gives my the wanted result. But rendering to a FrameBuffer doesn't work.
`
public void flush () {
flushes++;
buffer.flip();
MESH.setVertices(buffer);
if (lastTexture != null) {
lastTexture.bind();
}
MESH.draw();
buffer.clear();
}
/** Begins the SpriteBatch. */
public void begin () {
if (rendering) {
new IllegalStateException("You have to SpriteBatch.end() before calling begin again!").printStackTrace();
}
flushes = 0;
rendering = true;
shader.bind();
shader.setUniformMat4f("projection", camera.getCombined());
shader.setUniformVec4("color", color);
}
/** Ends the SpriteBatch, also flushes it. */
public void end () {
if (!rendering) {
new IllegalStateException("You've to SpriteBatch.begin before ending the spritebatch").printStackTrace();
}
rendering = false;
flush();
RenderUtil.unbindShaders();
RenderUtil.unbindTextures();
}
/** Switches the textures and flushes the old one.
* #param t */
private void switchTextures (Texture t) {
if (lastTexture != null) {
flush();
}
lastTexture = t;
}
`
Going to create a small example. Should be up soon.
EDIT:
Problem wasn't my FrameBuffer. Was my SpriteBatch.
You need to clear your buffers while your FBO is bound, otherwise you're only clearing the color/stencil/depth buffer associated with your window. That is not the behavior you want, because the depth test in this scenario uses your FBO's depth rather than the window's (default framebuffer).
public void begin () is the most logical place to do this:
public void begin () {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GL11.glViewport(0, 0, width, height);
}