irrlicht + AntiGrain vector graphics

A forum to store posts deemed exceptionally wise and useful
Post Reply
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

irrlicht + AntiGrain vector graphics

Post by chronologicaldot »

I did a bit of digging to figure out how Anti-Grain Geometry (AGG) works and discovered it is very easy to integrate with Irrlicht. There is also some example code with loading system fonts, so one day I hope to use AGG to add font-loading capabilities to Irrlicht via IGUISkin.
Anyways, to show you how to use it, I wrote a tutorial.
https://magicsnippet.wordpress.com/2017 ... -irrlicht/

For those of you who want to cut to the chase (and read the example code because it gets chopped off on Wordpress), here is the example:

Code: Select all

 
#include <agg_basics.h>
#include <agg_rendering_buffer.h>
#include <agg_rasterizer_scanline_aa.h>
#include <agg_scanline_p.h>
#include <agg_renderer_scanline.h>
 
// Color
//#include <agg_pxlfmt_rgba.h>
 
// For unclosed curve
#include <agg_conv_unclose_polygon.h>
 
// Specifically for the curve
#include <agg_conv_stroke.h>
#include <agg_conv_bspline.h>
 
// To save time picking a color
#define AGG_ARGB32
#include <examples/pixel_formats.h>
 
//
#include <irrlicht.h>
 
//
#include <math.h>
#define PI 3.1415926539
 
inline double
degToRad(double p) {
    return (p * PI) / 180.;
}
 
class SineWavePoly {
    agg::point_d position;
    unsigned increment;
    double amplitude;
    double phase;
    unsigned startX;
    unsigned endX;
 
public:
    SineWavePoly()
        : position(0,0)
        , increment(0)
        , amplitude(10)
        , phase(0)
        , startX(0)
        , endX(0)
    {}
 
    SineWavePoly&
    setPosition(double nx, double ny) {
        position.x = nx;
        position.y = ny;
        return *this;
    }
 
    SineWavePoly&
    setAmplitude(double a) {
        amplitude = a;
        return *this;
    }
 
    SineWavePoly&
    setPhase(double p) {
        phase = degToRad(p);
        return *this;
    }
 
    SineWavePoly&
    setStartX(unsigned x) {
        startX = x;
        return *this;
    }
 
    SineWavePoly&
    setEndX(unsigned x) {
        endX = x;
        return *this;
    }
 
    void rewind(unsigned) {
        increment = 0;
    }
 
    unsigned vertex(double* x, double* y) {
        *x = double(startX + increment);
        double tx = degToRad(*x);
        *x = *x + position.x;
        *y = amplitude * sin(tx + phase) + position.y;
        if ( increment > endX )
            return agg::path_cmd_stop;
        if ( increment == endX ) {
            ++increment;
            return agg::path_cmd_end_poly;
        }
        if ( increment == 0 ) {
            ++increment;
            return agg::path_cmd_move_to;
        }
        ++increment;
        return agg::path_cmd_line_to;
    }
};
 
bool renderPolygon(irr::video::IImage* pImage, double resolution) {
    if (!pImage)
        return false;
 
    // Create AGG stuff, setting the Irrlicht image->getData() as the buffer
    SineWavePoly sinePoly;
    sinePoly.setPosition(10,250).setStartX(0).setEndX(480).setAmplitude(50);
 
    // Yes, it's technically u32 data, but Anti-Grain treats it as pixels composed of 1 byte/8 bit colors.
    irr::u8* imgDataPtr = (irr::u8*) pImage->getData();
    irr::core::dimension2du imgSize = pImage->getDimension();
 
    agg::rendering_buffer renderingBuffer;
    renderingBuffer.attach(imgDataPtr, imgSize.Width, imgSize.Height, pImage->getPitch());
    // Alternatively:
    //agg::rendering_buffer renderingBuffer( ... same args as passed to .attach() );
 
    agg::pixfmt_bgra32 pixelFormat(renderingBuffer);
    agg::renderer_base<agg::pixfmt_bgra32> rendererBase(pixelFormat);
    agg::scanline_p8 scanLine;
    agg::rasterizer_scanline_aa<> rasterizerAA;
 
    // function-scope-limited typedef
    typedef agg::conv_bspline conv_bspline_type;
 
    // Normal version ****
    //rasterizerAA.add_path(sinePoly);
 
    // Unfilled, thick lines with controllable ends ****
    agg::conv_stroke convStrokeSinePoly(sinePoly);
    convStrokeSinePoly.width(2.0);
    rasterizerAA.add_path(convStrokeSinePoly);
    agg::render_scanlines_aa_solid(rasterizerAA, scanLine, rendererBase, agg::rgba(0.0, 0.7, 0.9));
 
    // Bezier version ****
    //conv_bspline_type sineBspline(sinePoly);
    //sineBspline.interpolation_step(1.0 / resolution);
    //agg::conv_stroke sineStroke(sineBspline);
    //sineStroke.width(3.0);
    //rasterizerAA.add_path(sineStroke);
    //agg::render_scanlines_aa_solid(rasterizerAA, scanLine, rendererBase, agg::rgba(1,1,1));
 
    return true;
}
 
int main() {
    irr::core::dimension2du screenSize(500,500);
    irr::video::SColor white(0xffffffff);
    irr::core::vector2di zeroVector(0);
 
    irr::IrrlichtDevice* device = irr::createDevice(irr::video::EDT_BURNINGSVIDEO, screenSize);
 
    if ( !device ) return 1;
 
    irr::video::IVideoDriver* videoDriver = device->getVideoDriver();
 
    videoDriver->setTextureCreationFlag(irr::video::ETCF_CREATE_MIP_MAPS, false);
    videoDriver->setTextureCreationFlag(irr::video::ETCF_ALLOW_NON_POWER_2, true);
 
    irr::core::recti srcRect(0,0,(irr::s32)screenSize.Width,(irr::s32)screenSize.Height);
 
    irr::video::IImage* img = videoDriver->createImage(irr::video::ECF_A8R8G8B8, screenSize);
 
    if (! renderPolygon(img, 20.0) )
        return 1;
    irr::video::ITexture* tex = videoDriver->addTexture(irr::io::path("image name"), img);
 
    while ( device->run() ) {
        videoDriver->beginScene();
        videoDriver->draw2DImage(tex, zeroVector, srcRect, 0, white, true);
        videoDriver->endScene();
    }
 
    img->drop();
 
    device->drop();
    return 0;
}
 
Last edited by chronologicaldot on Fri Dec 08, 2017 8:24 pm, edited 2 times in total.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: irrlicht + AntiGrain vector graphics

Post by Mel »

Kewl! vector graphics should be a must for every graphics API! keep the good job :)
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: irrlicht + AntiGrain vector graphics

Post by CuteAlien »

Interesting, I didn't know anti-grain. You could add some links in your blog-post (like to anti-grain and Irrlicht).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Re: irrlicht + AntiGrain vector graphics

Post by netpipe »

why was the example source includes not present in the code example ?
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: irrlicht + AntiGrain vector graphics

Post by chronologicaldot »

@tecan - Oops! It didn't copy correctly for some reason. Probably because Wordpress thought they were HTML tags. Let me add them again for ya.

@CuteAlien - Thanks for the suggestion. I usually do add those links, so that was oversight on my part. Thanks for catching that.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: irrlicht + AntiGrain vector graphics

Post by chronologicaldot »

@tecan - I updated my first post for you. Now I just need to update the Wordpress page.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: irrlicht + AntiGrain vector graphics

Post by chronologicaldot »

Update: Sorry. Use agg::pixfmt_bgra32, not agg::pixfmt_argb32. I forgot the fact that Anti-Grain accesses colors by index, not by bit-shift, means that you have to pick the reverse pattern as the largest bit, which I believe has to do with the endianness of my machine. I seem to recall having little endian.
Rusty Rooster
Posts: 11
Joined: Thu May 24, 2018 6:43 pm
Location: USA

Re: irrlicht + AntiGrain vector graphics

Post by Rusty Rooster »

Anti Grain is a great library! I use it for everything
LunaRebirth
Posts: 386
Joined: Sun May 11, 2014 12:13 am

Re: irrlicht + AntiGrain vector graphics

Post by LunaRebirth »

Thread reviving :shock:

But do you happen to have experience with loading AntiGrain images as Irrlicht textures?

Here's my full code:

Code: Select all

#pragma warning(disable : 4996)

#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#define AGG_BGRA32
#include <agg-src/include/agg_basics.h>
#include <agg-src/include/agg_rendering_buffer.h>
#include <agg-src/include/agg_rasterizer_scanline_aa.h>
#include <agg-src/include/agg_scanline_p.h>
#include <agg-src/include/agg_renderer_scanline.h>

// To save time picking a color
#define AGG_ARGB32
#include <agg-src/examples/pixel_formats.h>

#include "agg_scanline_p.h"
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_pixfmt_rgba.h"
#include "agg-src/examples/svg_viewer/agg_svg_parser.h"
#include "platform/agg_platform_support.h"
#include "ctrl/agg_slider_ctrl.h"

//
#include <irrlicht.h>

//
#include <string>

enum { flip_y = false };

typedef agg::row_accessor<irr::u32> rendering_buffer_u32;
typedef agg::pixfmt_alpha_blend_rgba<agg::blender_bgra32, rendering_buffer_u32> agg_pixel_type;

class the_application : public agg::platform_support
{
    agg::slider_ctrl<color_type> m_expand;
    agg::slider_ctrl<color_type> m_gamma;
    agg::slider_ctrl<color_type> m_scale;
    agg::slider_ctrl<color_type> m_rotate;

    double m_min_x;
    double m_min_y;
    double m_max_x;
    double m_max_y;

    double m_x;
    double m_y;
    double m_dx;
    double m_dy;
    bool   m_drag_flag;

public:
    agg::svg::path_renderer m_path;

    the_application(agg::pix_format_e format, bool flip_y) :
        agg::platform_support(format, flip_y),
        m_path(),
        m_expand(1, 1, 10, 10, !flip_y),
        m_gamma(0, 0, 0, 0, !flip_y),
        m_scale(0, 0, 0, 0, !flip_y),
        m_rotate(256 + 5, 5 + 15, 512 - 5, 11 + 15, !flip_y),
        m_min_x(0.0),
        m_min_y(0.0),
        m_max_x(0.0),
        m_max_y(0.0),
        m_x(0.0),
        m_y(0.0),
        m_dx(0.0),
        m_dy(0.0),
        m_drag_flag(false)
    {
        add_ctrl(m_expand);
        add_ctrl(m_gamma);
        add_ctrl(m_scale);
        add_ctrl(m_rotate);

        m_expand.label("Expand=%3.2f");
        m_expand.range(-1, 1.2);
        m_expand.value(0.0);

        m_gamma.label("Gamma=%3.2f");
        m_gamma.range(0.0, 3.0);
        m_gamma.value(1.0);

        m_scale.label("Scale=%3.2f");
        m_scale.range(0.2, 10.0);
        m_scale.value(1.0);

        m_rotate.label("Rotate=%3.2f");
        m_rotate.range(-180.0, 180.0);
        m_rotate.value(0.0);
    }

    void parse_svg(const char* fname)
    {
        agg::svg::parser p(m_path);
        p.parse(fname);
        m_path.bounding_rect(&m_min_x, &m_min_y, &m_max_x, &m_max_y);
    }

    ITexture* agg_svg_ITexture(IVideoDriver* driver, fschar_t* texture_name = (fschar_t*)"", double scale_value = 0.0, int stride_value = 1, double rotate_value = 0.0, double expand_value = 0.1, video::ECOLOR_FORMAT color_format = ECF_A8R8G8B8, u32 alpha_value = 0)
    {
        typedef agg::pixfmt_bgra32 pixfmt;
        typedef agg::renderer_base<pixfmt> renderer_base;
        typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;

        /// PROBLEM IS HERE? image_size cannot be anything other than around 2048,2048
        dimension2du image_size(2048,2048);
        IImage* image = driver->createImage(color_format, image_size);
        agg::rendering_buffer rbuf((agg::int8u*)image->lock(), image_size.Width, image_size.Height, image_size.Width * stride_value);

        pixfmt pixf(rbuf);
        renderer_base rb(pixf);
        renderer_solid ren(rb);

        rb.clear(agg::rgba(1, 1, 1, 0));

        agg::rasterizer_scanline_aa<> ras;
        agg::scanline_p8 sl;
        agg::trans_affine mtx;

        mtx *= agg::trans_affine_scaling(m_scale.value(), m_scale.value() + 2.5);

        m_path.render(ras, sl, ren, mtx, rb.clip_box(), 1.0);

        agg::render_scanlines(ras, sl, ren);

        image->unlock();
        ITexture* texture = driver->addTexture(texture_name, image);
        image->drop();
        return texture;
    }

    ~the_application() {

    }

};

agg::svg::path_renderer* agg_svg_path(char* file_name)
{
    agg::svg::path_renderer* m_path = new agg::svg::path_renderer();
    agg::svg::parser p(*m_path);
    p.parse(file_name);
    return m_path;
}

IGUIImage* img = 0;
void createImageTest(the_application* app, IrrlichtDevice* device)
{
    if (img)
        img->remove();

    const char* fname = "tiger.svg";
    app->parse_svg(app->full_file_name(fname));

    ITexture* tex = app->agg_svg_ITexture(device->getVideoDriver(), (fschar_t*)fname);
    img = device->getGUIEnvironment()->addImage(tex, vector2di(0, 0));
    img->setSourceRect(rect<s32>(0,0,tex->getOriginalSize().Width/4,tex->getOriginalSize().Height/4));
}

int main()
{
    irr::core::dimension2du screenSize(500, 500);
    irr::video::SColor white(0xffffffff);

    irr::IrrlichtDevice* device = irr::createDevice(irr::video::EDT_OPENGL, screenSize);

    if (!device) return 1;

    irr::video::IVideoDriver* videoDriver = device->getVideoDriver();

    the_application app(agg::pix_format_bgra32, flip_y);
    createImageTest(&app, device);

    while (device->run()) {
        videoDriver->beginScene(true, false, white);

        device->getGUIEnvironment()->drawAll();

        videoDriver->endScene();
    }

    device->drop();
    return 0;
}
I marked in the code above "/// PROBLEM IS HERE? image_size cannot be anything other than around 2048,2048".
Basically, so long as my image is 2048,2048 (I say around there, because 2000,2000 also works) my image looks great.
See here:
Image

But you may also notice that in createImageTest(), I divide the texture width/height by 4.
This is because at 2048x2048, Irrlicht or AGG creates 4 of the image next to and above/below each other (12 total). Not sure why. So I "cut" them out.

Here's an example of if I make the image 1024x1024 instead:
Image
Again, this is expecting there to be some bug that displays 12 images, so it cuts the width/height by 4.
However, this band-aid solution no longer seems to work as the images are intersecting each other.

Here's what it looks like if I stop trying to cut the image all together:
Image
And the more I scale it, the more images intersect and appear together.

Any ideas what I might be doing wrong?
LunaRebirth
Posts: 386
Joined: Sun May 11, 2014 12:13 am

Re: irrlicht + AntiGrain vector graphics

Post by LunaRebirth »

(not sure why my comment posted twice, but I couldn't remove it so this is the best I could do)
Last edited by LunaRebirth on Wed Sep 15, 2021 2:05 am, edited 1 time in total.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: irrlicht + AntiGrain vector graphics

Post by chronologicaldot »

Here's what's going on so far as I can tell.
AGG is going to draw the Tiger the same size no matter what, even if you draw on a huge canvas. You may consider exporting the image just to see what size the Tiger is. AGG doesn't have a resize. You'll need to manually adjust the vector graphic data points' scale.
From what I recall, img->setSourceRect() is going to select just the partial area of the image, so if you pick anything less than the original SVG's size, it's going to chop it off.
I also noticed that you're stride is set to 1 * image width. The stride to AGG is in bytes, and since you're using 32 bits, that's 4 bytes, so try setting the stride to 4.
Those are my initial thoughts.
Post Reply