draw 2d batch, Rectangles as destinations

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

draw 2d batch, Rectangles as destinations

Post by Mel »

Watching the current implementation of Irrlicht, it had a functionality to draw batches of rectangles from images -textures- into positions, which meant that the initial size of the rectangle was the same as the final. I thought it lacked a method that, besides drawing batches of images, also, allowed them to be scaled arbitrarily.

This method is basically a rewrite of the drawing2dbatch function, but using rectangles, and clipping them against the current rendertarget and, if provided, a clipping rectangle.

Note that i have only tested it with the screen resolution, and not the clipping against a rectangle, so it is posible it is buggy.

The main reason to make things like this is that the method for filtering 2D drawing routines, while it is useful it is also a bit limiting due to the fact that you have to enable and disable it between drawing calls, and that seems to be slow. This way, not only you draw and scale, it is also filtered and it draws several images at once.

I hope you find it useful!

Code: Select all

 
void CD3D9Driver::draw2DImageBatch(const video::ITexture* texture,
                const core::array<core::rect<s32> >& destinationRects, //positions
                const core::array<core::rect<s32> >& sourceRects,
                const core::rect<s32>* clipRect,
                SColor color,
                bool useAlphaChannelOfTexture)
{
    if (!texture)
        return;
 
    if (!setActiveTexture(0, const_cast<video::ITexture*>(texture)))
        return;
 
    const irr::u32 drawCount = core::min_<u32>(destinationRects.size(), sourceRects.size());
 
    core::array<S3DVertex> vtx(drawCount * 4);
    core::array<u16> indices(drawCount * 6);
 
    for(u32 i = 0;i < drawCount;i++)
    {
        core::position2d<s32> targetPos = destinationRects[i].UpperLeftCorner;
        core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
        // This needs to be signed as it may go negative.
        core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
        core::dimension2d<s32> destinationSize(destinationRects[i].getSize());
        const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
        s32 originalSize,shift;
 
        core::rect<s32> currentClipRect(0,0,renderTargetSize.Width,renderTargetSize.Height);
 
        //The original clip is the screen, or the current renderTarget.
        //This way we supress extra, and potentially undesirable modifications to the sizes
        //It is actually faster (no extra divisions or multiplications, only additions and substractions)
        //It only relies on Irrlitch code, which is portable
        //And we make sure the clipping works as expected in EVERY situation.
 
        if(clipRect){
            if(clipRect->UpperLeftCorner.X > currentClipRect.UpperLeftCorner.X){ currentClipRect.UpperLeftCorner.X=clipRect->UpperLeftCorner.X;}
            if(clipRect->UpperLeftCorner.Y > currentClipRect.UpperLeftCorner.Y){ currentClipRect.UpperLeftCorner.Y=clipRect->UpperLeftCorner.Y;}
            if(clipRect->LowerRightCorner.X < currentClipRect.LowerRightCorner.X){ currentClipRect.LowerRightCorner.X=clipRect->LowerRightCorner.X;}
            if(clipRect->LowerRightCorner.Y < currentClipRect.LowerRightCorner.Y){ currentClipRect.LowerRightCorner.Y=clipRect->LowerRightCorner.Y;}
        }
        //We intersect the clipping rectangles here.
 
        if (targetPos.X < currentClipRect.UpperLeftCorner.X)
        {
            shift=(targetPos.X-currentClipRect.UpperLeftCorner.X)*sourceSize.Width/destinationSize.Width;
 
                destinationSize.Width += targetPos.X - currentClipRect.UpperLeftCorner.X;
                if (destinationSize.Width <= 0)
                    continue;
 
                sourcePos.X -= shift;
                sourceSize.Width += shift;
                targetPos.X = currentClipRect.UpperLeftCorner.X;
        }
 
        if (targetPos.X + (s32)destinationSize.Width > currentClipRect.LowerRightCorner.X)
        {
            originalSize=destinationSize.Width;
            destinationSize.Width -= (targetPos.X + destinationSize.Width) - currentClipRect.LowerRightCorner.X;
            if (destinationSize.Width <= 0)
                continue;
 
            sourceSize.Width = destinationSize.Width*sourceSize.Width/originalSize;
        }
 
        if (targetPos.Y < currentClipRect.UpperLeftCorner.Y)
        {
            shift=(targetPos.Y-currentClipRect.UpperLeftCorner.Y)*sourceSize.Height/destinationSize.Height;
            destinationSize.Height += targetPos.Y - currentClipRect.UpperLeftCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourcePos.Y -= shift;
            sourceSize.Height += shift;
            targetPos.Y = currentClipRect.UpperLeftCorner.Y;
        }
 
        if (targetPos.Y + (s32)destinationSize.Height > currentClipRect.LowerRightCorner.Y)
        {
            originalSize=destinationSize.Height;
            destinationSize.Height -= (targetPos.Y + destinationSize.Height) - currentClipRect.LowerRightCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourceSize.Height = destinationSize.Height*sourceSize.Height/originalSize;
        }
 
        // ok, we've clipped everything.
        // now draw it.
 
        core::rect<f32> tcoords;
        tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ;
        tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height;
        tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width);
        tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height);
 
        const core::rect<s32> poss(targetPos, destinationSize);
 
        setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture);
 
        vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, color,
                tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, color,
                tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, color,
                tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
        vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, color,
                tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
 
        const u32 curPos = vtx.size()-4;
        indices.push_back(0+curPos);
        indices.push_back(1+curPos);
        indices.push_back(2+curPos);
 
        indices.push_back(0+curPos);
        indices.push_back(2+curPos);
        indices.push_back(3+curPos);
    }
 
    if (vtx.size())
    {
        setVertexShader(EVT_STANDARD);
 
        pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vtx.size(), indices.size() / 3, indices.pointer(),
            D3DFMT_INDEX16,vtx.pointer(), sizeof(S3DVertex));
    }
}
 
it is DX9 only, for the moment.

EDIT: added driver independent version.

Code: Select all

 
void draw2DScaledBatch(
    video::ITexture* texture,
    const core::array<core::recti>& destinationRects,
    const core::array<core::recti>& sourceRects,
    const core::recti* clipRect,
    video::SColor& col,
    bool useAlpha)
{
    if(!texture)
    {
        return;
    }
   
        const core::dimension2d<u32>& renderTargetSize = drv->getCurrentRenderTargetSize();
    video::SMaterial mat;
 
    mat.setTexture(0,texture);
 
    //IF there was another option on the parameters, we could allow more blending modes for the 2D drawings
    //such as additive or substractive blending, besides the regular alpha blend. It is left like this to achieve the same      //effect of the regular 2D functions of Irrlicht, but can be easily modified. exercise left to the reader :)
 
    mat.MaterialType = useAlpha ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
    mat.ZWriteEnable = false;
    mat.Lighting = false;
    mat.FrontfaceCulling = false;
    mat.BackfaceCulling = false;
    mat.MaterialTypeParam = 0;
 
    const u32 drawCount = core::min_<u32>(destinationRects.size(), sourceRects.size());
 
    core::array<video::S3DVertex> vtx(drawCount * 4);
    core::array<u16> indices(drawCount * 6);
 
    for(u32 i = 0;i < drawCount;i++)
    {
        core::position2d<s32> targetPos = destinationRects[i].UpperLeftCorner;
        core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
 
        // This needs to be signed as it may go negative.
 
        core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
        core::dimension2d<s32> destinationSize(destinationRects[i].getSize());
        s32 originalSize,shift;
 
        core::rect<s32> currentClipRect(0,0,renderTargetSize.Width,renderTargetSize.Height);
 
        //The original clip is the screen, or the current renderTarget.
        //This way we supress extra modifications to the sizes
        //And we make sure the clipping works as expected.
 
        if(clipRect)
        {
            if(clipRect->UpperLeftCorner.X > currentClipRect.UpperLeftCorner.X)
            {
                currentClipRect.UpperLeftCorner.X=clipRect->UpperLeftCorner.X;
            }
 
            if(clipRect->UpperLeftCorner.Y > currentClipRect.UpperLeftCorner.Y)
            {
                currentClipRect.UpperLeftCorner.Y=clipRect->UpperLeftCorner.Y;
            }
 
            if(clipRect->LowerRightCorner.X < currentClipRect.LowerRightCorner.X)
            {
                currentClipRect.LowerRightCorner.X=clipRect->LowerRightCorner.X;
            }
 
            if(clipRect->LowerRightCorner.Y < currentClipRect.LowerRightCorner.Y)
            {
                currentClipRect.LowerRightCorner.Y=clipRect->LowerRightCorner.Y;
            }
        }
        //Then, we intersect the clipping rects here.
 
        if (targetPos.X < currentClipRect.UpperLeftCorner.X)
        {
            shift=(targetPos.X-currentClipRect.UpperLeftCorner.X)*sourceSize.Width/destinationSize.Width;
 
                destinationSize.Width += targetPos.X - currentClipRect.UpperLeftCorner.X;
                if (destinationSize.Width <= 0)
                    continue;
 
                sourcePos.X -= shift;
                sourceSize.Width += shift;
                targetPos.X = currentClipRect.UpperLeftCorner.X;
        }
 
        if (targetPos.X + (s32)destinationSize.Width > currentClipRect.LowerRightCorner.X)
        {
            originalSize=destinationSize.Width;
            destinationSize.Width -= (targetPos.X + destinationSize.Width) - currentClipRect.LowerRightCorner.X;
            if (destinationSize.Width <= 0)
                continue;
 
            sourceSize.Width = destinationSize.Width*sourceSize.Width/originalSize;
        }
 
        if (targetPos.Y < currentClipRect.UpperLeftCorner.Y)
        {
            shift=(targetPos.Y-currentClipRect.UpperLeftCorner.Y)*sourceSize.Height/destinationSize.Height;
            destinationSize.Height += targetPos.Y - currentClipRect.UpperLeftCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourcePos.Y -= shift;
            sourceSize.Height += shift;
            targetPos.Y = currentClipRect.UpperLeftCorner.Y;
        }
 
        if (targetPos.Y + (s32)destinationSize.Height > currentClipRect.LowerRightCorner.Y)
        {
            originalSize=destinationSize.Height;
            destinationSize.Height -= (targetPos.Y + destinationSize.Height) - currentClipRect.LowerRightCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourceSize.Height = destinationSize.Height*sourceSize.Height/originalSize;
        }
 
        // ok, we've soft' clipped everything.
        // now draw it.
 
        core::rect<f32> tcoords;
        tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ;
        tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height;
        tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width);
        tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height);
 
        core::rect<s32> poss(targetPos, destinationSize);
 
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
       
        const u32 curPos = vtx.size()-4;
        indices.push_back(0+curPos);
        indices.push_back(1+curPos);
        indices.push_back(2+curPos);
 
        indices.push_back(0+curPos);
        indices.push_back(2+curPos);
        indices.push_back(3+curPos);
    }
 
    if(vtx.size())
    {
        drv->setMaterial(mat);
       
        core::matrix4 m;
        drv->setTransform(video::ETS_WORLD, m);
 
        m.setTranslation(core::vector3df(0.5,0.5,0));
        drv->setTransform(video::ETS_VIEW, m);
 
        m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
        m.setTranslation(core::vector3df(-1,1,0));
        drv->setTransform(video::ETS_PROJECTION, m);
       
        drv->drawIndexedTriangleList(vtx.pointer(),vtx.size(),indices.pointer(),indices.size()/3);
    }
}
 
Last edited by Mel on Tue Sep 11, 2012 2:27 pm, edited 1 time in total.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Bump: The code is now more robust and realiable. The clipping works correctly, and on every situation. Instead of two clipping operations, the code is reduced to make only one, and the clip rectangle, if it is provided, is intersected with the rendertarget rectangle, and only the intersection of both rectangles is used then for the drawing.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Would I be correct in guessing that this function is one we'd need to compile into the engine, or throw into a separate header?
Is it true that this function will resize the ITexture that the pointer is pointing to to? I'm confused as to what the destination rectangles are for: Will it generate mutiple sizes of textures, somehow?

Could a lot of these arguments be defaulted if you want a simple resize?

This looks like a wonderfully useful chunk of code, I just want to understand what it does\how it does it.
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

Yes, it should be added to the engine manually, and the engine should be recompiled... That implementation is currently DX only :/ If Irrlicht is able to draw triangle batches on its own, it is posible to change it quickly so it became an external function...

You need that both rectangle arrays have the same amount of rectangles. The source rectangles mark the fragments of the original ITexture that will be drawn, and the destination rectangles mark the positions where those fragments are going to be drawn. The good thing is that the destination rectangles don't need to be of the same size of the source rectangles, making it posible that the textures are drawn rescaled.

There aren't that much parameters, only 2 rectangle arrays, a color reference and a boolean. The real pain comes from filling the rectangle arrays actually :)

http://www.youtube.com/watch?v=Gyk_ZX2dE1Y

The main usage is to be able to create a single design for a 2D environment, and to be able to rescale it. That video shows an interface which is designed in 1024x768 and is rendered in 1280x720. The original texture is always of 1024x1024, It is posible to draw always the same interface in diferent resolutions without the need for rendertargets, for instance.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Aww... DX only? What would need to be changed to support OpenGL? When you say:
If Irrlicht is able to draw triangle batches on its own, it is posible to change it quickly so it became an external function...
Do you mean that this can be made driver-independant if something is changed in Irrlicht?

"Fragments"? If I am understanding you correctly, the source rectangles define what area of the original ITexture you want to "cut out"? And then the destination rects are on a UV of 0.1 - 1.0 where they should be put back? If this is correct, then why are there multiple rects to do this with? If you're editing the ITexture around, then why?

So, the color reference and boolean tell you the color of the alpha channel?

I think your usage that you listed is what I'm after: For the main Menu, and maybe for my in-game HUD, rescaling it to fit the screen. I saw a project with how to rescale fonts...
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

No, there is nothing to be changed in Irrlicht, it can be rewritten to be driver independent, and probably, the performance would be pretty much the same.

http://irrlicht.sourceforge.net/docu/cl ... e16ad8dde2

I would only hope that the vertex destination coordinates could be the same, and it would be done just changing one single line of code... or almost...

No, i am not editing the original texture, it remains unchanged. and the destination rectangles are in screen coordinates, not in texture coordinates. The original idea behind this is that if i have to draw in diferent resolutions, there is no need to have diferent textures with diferent sizes, you only use one texture, and scale it up or down as needed during the drawing process.

The process of 2D drawing is done using sets of screen aligned rectangles, with the proper coordinates. So, what this method does is to build the set of rectangles, clip them against the screen and the provided rectangle, if so, convert the screen coordinates to texture space coordinates to match the original portions of the image we want to draw, and finally, send them to the video driver.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

Mmm... I don't know if this would work properly under OpenGL, but i don't see any reason why it shouldn't, though i haven't tested it. But under DX, this code works flawlessly, doesn't require engine recompilation! and would allow more effects with the 2D graphics with a small modification...

Code: Select all

 
void draw2DScaledBatch(
    video::ITexture* texture,
    const core::array<core::recti>& destinationRects, 
    const core::array<core::recti>& sourceRects, 
    const core::recti* clipRect,
    video::SColor& col, 
    bool useAlpha)
{
    if(!texture)
    {
        return;
    }
    
        const core::dimension2d<u32>& renderTargetSize = drv->getCurrentRenderTargetSize();
    video::SMaterial mat;
 
    mat.setTexture(0,texture);
 
    //IF there was another option on the parameters, we could allow more blending modes for the 2D drawings
    //such as additive or substractive blending, besides the regular alpha blend. It is left like this to achieve the same      //effect of the regular 2D functions of Irrlicht, but can be easily modified. exercise left to the reader :)
 
    mat.MaterialType = useAlpha ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
    mat.ZWriteEnable = false;
    mat.Lighting = false;
    mat.FrontfaceCulling = false;
    mat.BackfaceCulling = false;
    mat.MaterialTypeParam = 0;
 
    const u32 drawCount = core::min_<u32>(destinationRects.size(), sourceRects.size());
 
    core::array<video::S3DVertex> vtx(drawCount * 4);
    core::array<u16> indices(drawCount * 6);
 
    for(u32 i = 0;i < drawCount;i++)
    {
        core::position2d<s32> targetPos = destinationRects[i].UpperLeftCorner;
        core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
 
        // This needs to be signed as it may go negative.
 
        core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
        core::dimension2d<s32> destinationSize(destinationRects[i].getSize());
        s32 originalSize,shift;
 
        core::rect<s32> currentClipRect(0,0,renderTargetSize.Width,renderTargetSize.Height);
 
        //The original clip is the screen, or the current renderTarget.
        //This way we supress extra modifications to the sizes
        //And we make sure the clipping works as expected.
 
        if(clipRect)
        {
            if(clipRect->UpperLeftCorner.X > currentClipRect.UpperLeftCorner.X)
            { 
                currentClipRect.UpperLeftCorner.X=clipRect->UpperLeftCorner.X;
            }
 
            if(clipRect->UpperLeftCorner.Y > currentClipRect.UpperLeftCorner.Y)
            { 
                currentClipRect.UpperLeftCorner.Y=clipRect->UpperLeftCorner.Y;
            }
 
            if(clipRect->LowerRightCorner.X < currentClipRect.LowerRightCorner.X)
            { 
                currentClipRect.LowerRightCorner.X=clipRect->LowerRightCorner.X;
            }
 
            if(clipRect->LowerRightCorner.Y < currentClipRect.LowerRightCorner.Y)
            { 
                currentClipRect.LowerRightCorner.Y=clipRect->LowerRightCorner.Y;
            }
        }
        //Then, we intersect the clipping rects here.
 
        if (targetPos.X < currentClipRect.UpperLeftCorner.X)
        {
            shift=(targetPos.X-currentClipRect.UpperLeftCorner.X)*sourceSize.Width/destinationSize.Width;
 
                destinationSize.Width += targetPos.X - currentClipRect.UpperLeftCorner.X;
                if (destinationSize.Width <= 0)
                    continue;
 
                sourcePos.X -= shift;
                sourceSize.Width += shift;
                targetPos.X = currentClipRect.UpperLeftCorner.X;
        }
 
        if (targetPos.X + (s32)destinationSize.Width > currentClipRect.LowerRightCorner.X)
        {
            originalSize=destinationSize.Width;
            destinationSize.Width -= (targetPos.X + destinationSize.Width) - currentClipRect.LowerRightCorner.X;
            if (destinationSize.Width <= 0)
                continue;
 
            sourceSize.Width = destinationSize.Width*sourceSize.Width/originalSize;
        }
 
        if (targetPos.Y < currentClipRect.UpperLeftCorner.Y)
        {
            shift=(targetPos.Y-currentClipRect.UpperLeftCorner.Y)*sourceSize.Height/destinationSize.Height;
            destinationSize.Height += targetPos.Y - currentClipRect.UpperLeftCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourcePos.Y -= shift;
            sourceSize.Height += shift;
            targetPos.Y = currentClipRect.UpperLeftCorner.Y;
        }
 
        if (targetPos.Y + (s32)destinationSize.Height > currentClipRect.LowerRightCorner.Y)
        {
            originalSize=destinationSize.Height;
            destinationSize.Height -= (targetPos.Y + destinationSize.Height) - currentClipRect.LowerRightCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourceSize.Height = destinationSize.Height*sourceSize.Height/originalSize;
        }
 
        // ok, we've soft' clipped everything.
        // now draw it.
 
        core::rect<f32> tcoords;
        tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ;
        tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height;
        tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width);
        tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height);
 
        core::rect<s32> poss(targetPos, destinationSize);
 
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
        
        const u32 curPos = vtx.size()-4;
        indices.push_back(0+curPos);
        indices.push_back(1+curPos);
        indices.push_back(2+curPos);
 
        indices.push_back(0+curPos);
        indices.push_back(2+curPos);
        indices.push_back(3+curPos);
    }
 
    if(vtx.size())
    {
        drv->setMaterial(mat);
        
        core::matrix4 m;
        drv->setTransform(video::ETS_WORLD, m);
 
        m.setTranslation(core::vector3df(0.5,0.5,0));
        drv->setTransform(video::ETS_VIEW, m);
 
        m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
        m.setTranslation(core::vector3df(-1,1,0));
        drv->setTransform(video::ETS_PROJECTION, m);
        
        drv->drawIndexedTriangleList(vtx.pointer(),vtx.size(),indices.pointer(),indices.size()/3);
    }
}
 
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Thank you for the explanation and code.

Is this functon meant to be called as a part of the user of the engine's draw setup for the HUD\menu? Ex:

Code: Select all

video_driver->beginScene( ... );
video_driver->drawAll();
HUD->draw_HUD()  // Contains groups of draw2DScaledBatch() function calls
video_driver->endScene()
Like this? or..?
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

Yes! it is used like the Irrlicht draw2Dbatch methods. Between the Begin and End Scene.

What i have seen also is something very interesting. I could use shaders for 2D operations...
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Ahh.... okay... interesting... Thank you for your explanations; I think my project will be making use of your code for menus, as well as our player HUD.
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

Feel free, just add the video driver parameter video::IVideoDriver* drv to the driver independent version. I am trying to add those lines of code but the boards won't let me edit =.=U
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Aww... Noted, thanks!
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: draw 2d batch, Rectangles as destinations

Post by Mel »

Working demo! The independency in 2D drawing is quite hard because of the diference in the hardware drivers. Actually, all the irrlicht drivers are more affine to DirectX than to Open GL, so, switching the code depending on the driver is the only real way to achieve it, but they are just 2 lines of code, so, keeping it all together is simpler.

This is the working code of the draw2Dbatch.

Code: Select all

 
void draw2DBatch(
    video::IVideoDriver* drv,
    video::ITexture* texture,
    const core::array<core::recti>& destinationRects, 
    const core::array<core::recti>& sourceRects, 
    const core::recti* clipRect,
    video::SColor& col, 
    bool useAlpha)
{
    if(!drv)
    {
        return;
    }
 
    if(!texture)
    {
        return;
    }
 
    const core::dimension2d<u32>& renderTargetSize = drv->getCurrentRenderTargetSize();
    video::SMaterial mat;
 
    mat.setTexture(0,texture);
    //IF there was another option on the parameters, we could allow more blending modes for the 2D drawings
    //such as additive or substractive blending, besides the regular alpha blend.
    mat.MaterialType = useAlpha ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
    mat.ZWriteEnable = false;
    mat.Lighting = false;
    mat.FrontfaceCulling = false;
    mat.BackfaceCulling = false;
    mat.MaterialTypeParam = 0;
 
    const u32 drawCount = core::min_<u32>(destinationRects.size(), sourceRects.size());
 
    core::array<video::S3DVertex> vtx(drawCount * 4);
    core::array<u16> indices(drawCount * 6);
 
    for(u32 i = 0;i < drawCount;i++)
    {
        core::position2d<s32> targetPos = destinationRects[i].UpperLeftCorner;
        core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
 
        // This needs to be signed as it may go negative.
 
        core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
        core::dimension2d<s32> destinationSize(destinationRects[i].getSize());
 
        s32 originalSize,shift;
 
        core::rect<s32> currentClipRect(0,0,renderTargetSize.Width,renderTargetSize.Height);
 
        //The original clip is the screen, or the current renderTarget.
        //This way we supress extra modifications to the sizes
        //And we make sure the clipping works as expected.
 
        if(clipRect)
        {
            if(clipRect->UpperLeftCorner.X > currentClipRect.UpperLeftCorner.X)
            { 
                currentClipRect.UpperLeftCorner.X=clipRect->UpperLeftCorner.X;
            }
 
            if(clipRect->UpperLeftCorner.Y > currentClipRect.UpperLeftCorner.Y)
            { 
                currentClipRect.UpperLeftCorner.Y=clipRect->UpperLeftCorner.Y;
            }
 
            if(clipRect->LowerRightCorner.X < currentClipRect.LowerRightCorner.X)
            { 
                currentClipRect.LowerRightCorner.X=clipRect->LowerRightCorner.X;
            }
 
            if(clipRect->LowerRightCorner.Y < currentClipRect.LowerRightCorner.Y)
            { 
                currentClipRect.LowerRightCorner.Y=clipRect->LowerRightCorner.Y;
            }
        }
        //Then, we intersect the clipping rects here.
 
        if (targetPos.X < currentClipRect.UpperLeftCorner.X)
        {
            shift=(targetPos.X-currentClipRect.UpperLeftCorner.X)*sourceSize.Width/destinationSize.Width;
 
                destinationSize.Width += targetPos.X - currentClipRect.UpperLeftCorner.X;
                if (destinationSize.Width <= 0)
                    continue;
 
                sourcePos.X -= shift;
                sourceSize.Width += shift;
                targetPos.X = currentClipRect.UpperLeftCorner.X;
        }
 
        if (targetPos.X + (s32)destinationSize.Width > currentClipRect.LowerRightCorner.X)
        {
            originalSize=destinationSize.Width;
            destinationSize.Width -= (targetPos.X + destinationSize.Width) - currentClipRect.LowerRightCorner.X;
            if (destinationSize.Width <= 0)
                continue;
 
            sourceSize.Width = destinationSize.Width*sourceSize.Width/originalSize;
        }
 
        if (targetPos.Y < currentClipRect.UpperLeftCorner.Y)
        {
            shift=(targetPos.Y-currentClipRect.UpperLeftCorner.Y)*sourceSize.Height/destinationSize.Height;
            destinationSize.Height += targetPos.Y - currentClipRect.UpperLeftCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourcePos.Y -= shift;
            sourceSize.Height += shift;
            targetPos.Y = currentClipRect.UpperLeftCorner.Y;
        }
 
        if (targetPos.Y + (s32)destinationSize.Height > currentClipRect.LowerRightCorner.Y)
        {
            originalSize=destinationSize.Height;
            destinationSize.Height -= (targetPos.Y + destinationSize.Height) - currentClipRect.LowerRightCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourceSize.Height = destinationSize.Height*sourceSize.Height/originalSize;
        }
 
        // ok, we've soft' clipped everything.
        // now draw it.
 
        core::rect<f32> tcoords;
        tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ;
        tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height;
        tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width);
        tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height);
 
        core::rect<s32> poss(targetPos, destinationSize);
 
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
        
        const u32 curPos = vtx.size()-4;
        indices.push_back(0+curPos);
        indices.push_back(1+curPos);
        indices.push_back(2+curPos);
 
        indices.push_back(0+curPos);
        indices.push_back(2+curPos);
        indices.push_back(3+curPos);
    }
 
    if(vtx.size())
    {
        drv->setMaterial(mat);
        
        core::matrix4 m;
        drv->setTransform(video::ETS_WORLD, m);
 
        if(drv->getDriverType() == video::EDT_OPENGL)
        {
            m.setTranslation(core::vector3df(0.0,0.0,0.0));
        }
        else
        {
            m.setTranslation(core::vector3df(0.5,0.5,0.0));
        }
        
        drv->setTransform(video::ETS_VIEW, m);
 
        m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
        
        if(drv->getDriverType() == video::EDT_OPENGL)
        {
            m.setTranslation(core::vector3df(1,1,0));
        }
        else
        {
            m.setTranslation(core::vector3df(-1,1,0));
        }
 
        drv->setTransform(video::ETS_PROJECTION, m);
        
        drv->drawIndexedTriangleList(vtx.pointer(),vtx.size(),indices.pointer(),indices.size()/3);
    }
}
 
 
This is the demo running...

Image
ard because of the diference in the hardware drivers. Actually, all the irrlicht drivers are more affine to DirectX than to Open GL, so, switching the code depending on the driver is the only real way to achieve it, but they are just 2 lines of code, so, keeping it all together is simpler.

This is the working code of the draw2Dbatch.

Code: Select all

 
void draw2DBatch(
    video::IVideoDriver* drv,
    video::ITexture* texture,
    const core::array<core::recti>& destinationRects, 
    const core::array<core::recti>& sourceRects, 
    const core::recti* clipRect,
    video::SColor& col, 
    bool useAlpha)
{
    if(!drv)
    {
        return;
    }
 
    if(!texture)
    {
        return;
    }
 
    const core::dimension2d<u32>& renderTargetSize = drv->getCurrentRenderTargetSize();
    video::SMaterial mat;
 
    mat.setTexture(0,texture);
    //IF there was another option on the parameters, we could allow more blending modes for the 2D drawings
    //such as additive or substractive blending, besides the regular alpha blend.
    mat.MaterialType = useAlpha ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
    mat.ZWriteEnable = false;
    mat.Lighting = false;
    mat.FrontfaceCulling = false;
    mat.BackfaceCulling = false;
    mat.MaterialTypeParam = 0;
 
    const u32 drawCount = core::min_<u32>(destinationRects.size(), sourceRects.size());
 
    core::array<video::S3DVertex> vtx(drawCount * 4);
    core::array<u16> indices(drawCount * 6);
 
    for(u32 i = 0;i < drawCount;i++)
    {
        core::position2d<s32> targetPos = destinationRects[i].UpperLeftCorner;
        core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
 
        // This needs to be signed as it may go negative.
 
        core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
        core::dimension2d<s32> destinationSize(destinationRects[i].getSize());
 
        s32 originalSize,shift;
 
        core::rect<s32> currentClipRect(0,0,renderTargetSize.Width,renderTargetSize.Height);
 
        //The original clip is the screen, or the current renderTarget.
        //This way we supress extra modifications to the sizes
        //And we make sure the clipping works as expected.
 
        if(clipRect)
        {
            if(clipRect->UpperLeftCorner.X > currentClipRect.UpperLeftCorner.X)
            { 
                currentClipRect.UpperLeftCorner.X=clipRect->UpperLeftCorner.X;
            }
 
            if(clipRect->UpperLeftCorner.Y > currentClipRect.UpperLeftCorner.Y)
            { 
                currentClipRect.UpperLeftCorner.Y=clipRect->UpperLeftCorner.Y;
            }
 
            if(clipRect->LowerRightCorner.X < currentClipRect.LowerRightCorner.X)
            { 
                currentClipRect.LowerRightCorner.X=clipRect->LowerRightCorner.X;
            }
 
            if(clipRect->LowerRightCorner.Y < currentClipRect.LowerRightCorner.Y)
            { 
                currentClipRect.LowerRightCorner.Y=clipRect->LowerRightCorner.Y;
            }
        }
        //Then, we intersect the clipping rects here.
 
        if (targetPos.X < currentClipRect.UpperLeftCorner.X)
        {
            shift=(targetPos.X-currentClipRect.UpperLeftCorner.X)*sourceSize.Width/destinationSize.Width;
 
                destinationSize.Width += targetPos.X - currentClipRect.UpperLeftCorner.X;
                if (destinationSize.Width <= 0)
                    continue;
 
                sourcePos.X -= shift;
                sourceSize.Width += shift;
                targetPos.X = currentClipRect.UpperLeftCorner.X;
        }
 
        if (targetPos.X + (s32)destinationSize.Width > currentClipRect.LowerRightCorner.X)
        {
            originalSize=destinationSize.Width;
            destinationSize.Width -= (targetPos.X + destinationSize.Width) - currentClipRect.LowerRightCorner.X;
            if (destinationSize.Width <= 0)
                continue;
 
            sourceSize.Width = destinationSize.Width*sourceSize.Width/originalSize;
        }
 
        if (targetPos.Y < currentClipRect.UpperLeftCorner.Y)
        {
            shift=(targetPos.Y-currentClipRect.UpperLeftCorner.Y)*sourceSize.Height/destinationSize.Height;
            destinationSize.Height += targetPos.Y - currentClipRect.UpperLeftCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourcePos.Y -= shift;
            sourceSize.Height += shift;
            targetPos.Y = currentClipRect.UpperLeftCorner.Y;
        }
 
        if (targetPos.Y + (s32)destinationSize.Height > currentClipRect.LowerRightCorner.Y)
        {
            originalSize=destinationSize.Height;
            destinationSize.Height -= (targetPos.Y + destinationSize.Height) - currentClipRect.LowerRightCorner.Y;
            if (destinationSize.Height <= 0)
                continue;
 
            sourceSize.Height = destinationSize.Height*sourceSize.Height/originalSize;
        }
 
        // ok, we've soft' clipped everything.
        // now draw it.
 
        core::rect<f32> tcoords;
        tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getOriginalSize().Width ;
        tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getOriginalSize().Height;
        tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getOriginalSize().Width);
        tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getOriginalSize().Height);
 
        core::rect<s32> poss(targetPos, destinationSize);
 
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
        vtx.push_back(video::S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
                0.0f, 0.0f, 0.0f, col,
                tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
        
        const u32 curPos = vtx.size()-4;
        indices.push_back(0+curPos);
        indices.push_back(1+curPos);
        indices.push_back(2+curPos);
 
        indices.push_back(0+curPos);
        indices.push_back(2+curPos);
        indices.push_back(3+curPos);
    }
 
    if(vtx.size())
    {
        drv->setMaterial(mat);
        
        core::matrix4 m;
        drv->setTransform(video::ETS_WORLD, m);
 
        if(drv->getDriverType() == video::EDT_OPENGL)
        {
            m.setTranslation(core::vector3df(0.0,0.0,0.0));
        }
        else
        {
            m.setTranslation(core::vector3df(0.5,0.5,0.0));
        }
        
        drv->setTransform(video::ETS_VIEW, m);
 
        m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
        
        if(drv->getDriverType() == video::EDT_OPENGL)
        {
            m.setTranslation(core::vector3df(1,1,0));
        }
        else
        {
            m.setTranslation(core::vector3df(-1,1,0));
        }
 
        drv->setTransform(video::ETS_PROJECTION, m);
        
        drv->drawIndexedTriangleList(vtx.pointer(),vtx.size(),indices.pointer(),indices.size()/3);
    }
}
 
 
This is the demo running...

Image

And this is the demo itself... the source is included, so, it is posible to recompile it. It works under Win7, i can't test it on Linux, but there is nothing that prevented it from working there.

http://omegasection.g0dsoft.com/demos/draw2Dbatch.zip
And this is the demo itself... the source is included, so, it is posible to recompile it. It works under Win7, i can't test it on Linux, but there is nothing that prevented it from working there. The example is compiled using Irrlicht 1.7.3

http://omegasection.g0dsoft.com/demos/draw2Dbatch.zip
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Re: draw 2d batch, Rectangles as destinations

Post by hybrid »

You somehow did duplicate your own post here, maybe edit this a little bit ;-)
For 2d mesh rendering we have a separate function, called drawVertexPrimitiveList2D, which should do all the setup you manually coded here. So I guess it should be also possible without the driver checks.
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Re: draw 2d batch, Rectangles as destinations

Post by d3jake »

Hybrid: Do you mean that Irrlicht can already do part\all of this process?
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
Post Reply